用“易于改编”原则,提高编程水平,写出更高质量的代码

不管新手仍是资深开发者都会常常问一个问题,“怎么写好的代码?”,要知道怎么写好代码,首先咱们要知道怎么样才是好的代码。要有明确的目标,才能知道如何达成目标。在《程序员修炼之道》中提到的“ETC Principle” -- 易于改编原则。这个原则看似简单,可是咱们越是深刻思考越是以为“简约而不简单”。php

这篇文章里会详细解刨在实际产品研发中“易于改编”的缘由和怎么作到“易于改编”, 从而让咱们编写出更好的代码。前端

「一」程序为什么须要“易于改编”?

为什么代码必需要易于改编?由于一个系统是会随着一个产品的发展,每日有用户增加就会有一直作不完的需求。只要公司一直在运营着这个产品,需求就会随着公司的发展而改变。只要咱们开发者一直与时并进专研新技术,咱们就须要一直升级优化。程序员

只有了解清楚一个系统在一个生命周期中,具体什么会推进咱们程序改变,从中咱们才会更深入明白为何咱们的代码须要”易于改编“。算法

需求会变

不管咱们是研发任何系统,产品需求都是会一直变的。这个是永恒不变的命运。为何呢?编程

  1. 产品方向 --- 随着产品的营销,运营,发展会推进产品需求一直新增,修改,优化。
  2. 使用量 --- 随着产品的用户量级,数据量级,并发量级也会推进程序的架构和策略上的变更。
  3. 技术升级优化 --- 甚至是咱们使用的语言,框架,依赖包等升级也会引发咱们的代码须要适应。
  4. 技术债 --- 多是由于时间的限制,以前的代码重于实现而质量不佳。

因此咱们的代码会随着岁月的流逝一直在迭代升级优化。后端

“可快速更变”是一个软件的核心

近几年不少技术团队启用了敏捷迭代开发模式。什么是敏捷迭代呢?设计模式

敏捷迭代就是把开发周期缩短到1-4周。小步快跑的迅速迭代交付功能上线。敏捷迭代的流程分别以下:安全

  1. 肯定需求 - 与老板和市场确认需求和流程
  2. 需求评审 - 与开发同频需求里面的功能点和业务流程
  3. 技术反讲 - 开发与产品同频需求,保证双方理解无误区,开发也须要评估开发难度和开发时间
  4. 研发周期 - 开发人员开始投入研发直接到功能和需求开发完毕,转交给测试,在测试环境提测
  5. 测试周期 - 测试和开发人员开始排除缺陷,修复全部在开发过程产生的bug
  6. 验收/预发布周期 - 当测试在测试环境把全部bug排除掉后,当前迭代版本就会发布到预发布环境让市场和产品验收功能
  7. 发布正式 - 当验收经过后,当前迭代版本就能够部署上线到正式环境
  8. 正式回归测试 - 发布上线后,就会有正式回归测试,最后一道防线,保证系统加入的全部新功能都无问题
  9. 迭代总结 - 每一期迭代结束后都总结此次迭代遇到的问题,持续优化,提升效率

你想一想若是一个APP或者系统,几个月甚至一年才更新一次功能和升级。咱们用起来其实很枯燥的,甚至咱们会发现不少问题,还有不少功能能够便捷或者提高咱们的使用体验。可是这么久才更新一次,咱们还会对这个产品抱有但愿吗?(除了微信这种已经很成熟的应用,可是就算是微信也是有持续更新的)。微信

因此一个好的产品,是须要快速迭代,小步快跑的迅速迭代交付功能上线的。也是由于这样,功能就须要持续更新、升级和优化。天然咱们研发的代码就须要一直随着产品的变化而改编。并且仍是每1-4周就会升级优化一次。架构

🏆小总结一下:

  • 一个系统会随着产品的发展和迭代,一直走在改变和更新的道路上。
  • 由于系统一直在变,代码就须要响应系统的变化,持续的快速迭代升级优化。
  • 既然代码须要快速的更变和升级,那程序的“易于改编”性就必需要高。

「二」如何作到“易于改编”?

咱们深入懂得为何系统会一直在改变,那咱们就要知道怎么写代码才能让一个程序“易于改编”,然而在敏捷迭代中才能快速的响应需求的变化。若是想让咱们编写的程序更容易的响应需求改变、业务改变和逻辑改变等,咱们就要充分的给咱们的程序解刨逻辑

说到逻辑与业务的分解,首先要根据需求和功能深刻思考分析,而后对其进行一个架构的设计。最经常使用的方式就是把系统模块化,组件化等的系统架构设计。

模块设计 ---「Modular Design」

模块设计,就是以功能块为单位进行程序设计,实现其求解算法的方法称为模块化。模块化的目的是为了下降程序复杂度,使程序设计、调试和维护等操做简单化。

不管是前端开发仍是后端开发,咱们都有模块化和组件设计模式。使用模块设计来分解咱们的功能和逻辑,目的是为了下降程序的复杂度、利于调试、维护、修改和新增功能。

好比如今咱们要作个CMS(内容管理系统),咱们一块儿来尝试使用模块设计来分解这个系统的功能。


设计思路

首先咱们要理解一个内容管理系统有哪些功能,而后把每一个功能划入各个模块里。可是不少童鞋一开始接触一个系统,而后开始瓜分模块会以为无从入手,可能花了半天坐在电脑前思考🤔,可是半天都吐不出一个因此然来。接下来让咱们一块儿来学习一套逻辑思惟,让咱们之后更轻松架构一套模块设计吧!


一开始先思考这个系统的目的和使用场景,这个系统是用来作什么的?

一个内容管理系统,通常来讲都是用来发发文章,新闻,或者是一个官方网站的内容管理。那一定就有文章。那管理文章内容,须要什么功能呢?

文章模块 「Article 模块」

  • 增删查改文章
  • 文章草稿
  • 文章置顶

文章子模块 --- 分类 「Article Category 模块」

  • 增删查改分类
  • 文章图片

那这些与文章相关的功能是否是能够统一放在“Article”模块中统一管理,而后文章的模块中还有一个文章分类的子模块叫作“Category”


有文章了一定就须要有做者,那做者在系统中实际上是一个用户。那咱们就须要有用户模块了。 加上一个管理系统,一定就有管理员,做者,甚至是会员。走一波这个逻辑咱们就发现应该要有如下的功能点。

用户模块 「User 模块」

  • 用户增删查改
  • 用户身份管理
  • 用户权限管理
  • 会员等级管理

这么一来咱们就能够创建一个单独的User模块。这个模块主要是管理用户相关的信息和功能。


看到这里咱们应该对一个系统的模块构思有一点的概念了。这个时候产品经理过来给咱们提了一个需求,“咱们如今要在这个系统添加一个标签体系,专门用来管理文章标签的。”

那童鞋们,大家以为这个需求应该放入那个模块呢?🤔 ....

大家答对了!🎉这个是属于文章的一个子模块,Tag模块 --- 专门管理文章的标签,而后和每一篇文章有多对多关系的。因此标签模块概括入文章模块中。若是咱们的内容管理系统作的很大,里面有视频内容,图文文章等等。咱们能够在一开始就把这些统一概括入“内容模块”,也就是Content模块中。


前端模块设计

说到了这里前端的童鞋估计要举手咯🙋‍♂️,前端的咱们求关注呀!“前端是以页面和交互为单位,不可能和后端同样按功能逻辑来分解模块吧?” --- 这个童鞋说的在理哈。其实前端和后端的设计上是有稍微的不同的。

后端会以业务逻辑来分解模块,可是前端有页面和数据逻辑两块的代码。因此前端相对比后端就要分开两种模块分解思路了。

页 (排) 面 (版) 的模块设计

  • 前端的页面模块与产品定义的系统模块会更加贴切一些。前端分解的模块会跟用户所看到的操做功能分组。
  • 简单的模块分解,能够利用产品童鞋给到咱们的导航来分解,这样会更合理的规整咱们的页面模块。
  • 若是在页面功能上再想细分,那就能够用组件设计来分解了。

前端逻辑模块设计

  • 几年前的前端就是个“切图仔”,基本不用考虑什么业务逻辑,数据逻辑,数据交互这些技术领域。可是由于先后端分离如今已经变成大多数公司的研发策略。慢慢先后端都各自分摊了业务逻辑和数据交互等处理。

  • 由于前端也有大量的业务逻辑和交互逻辑,因此在咱们封装和解耦的时候,也会遇到须要分解模块来处理。如今最典型的例子就是在使用Vue的状态管理Vuex的时候,须要用到模块管理来分解逻辑,使后面维护和修改更容易。

  • 其实前端也是用后端同一套思惟模式来分解业务就能够了,以功能为单位来分解大家的模块就能够了。


解耦 - 「Decoupling」

解耦,就是把复杂繁琐的逻辑拆分红更小的逻辑块。从而让复杂的逻辑分解成小的逻辑处理,使得逻辑变得更简化,更易于调试和维护。

在一个功能众多、业务复杂和系统模块繁多的系统中,每个模块里面的代码也会开始变得臃肿,愈来愈难调试、维护和管理。其实模块化和解耦是一致的。模块化也是为了解耦你的程序。这里咱们重点讲的是模块之间和逻辑之间的解耦(Decouping)。

我分享一个经历让你们深入认知到解耦的重要性。我遇到过最夸张的有一段逻辑处理写了上5000行代码的童鞋,然而更可怕的是,在相同功能的地方那5000行代码被复制粘贴过来了。😱我滴乖乖,这位童鞋在研发小组中有个花名叫“复制兄”。不过获得你们的帮忙和提点下,后面他也成为了这个小组中的一名优秀的程序员。


若是咱们不懂得解耦代码,编写的代码会给咱们后面带来很重的“技术债”。假设一下,你的5000行处理逻辑,在上数十个地方使用了。咱们要改一下这段逻辑就难过登天了。就算是这段逻辑没有复用性,但当你须要回头去修改这段逻辑也是会让你头皮发麻,无从入手。修改一点这个逻辑均可能会致使出现10个bug的后果。

咱们深入知道解耦的重要性,那么咱们应该怎么去高效解耦代码呢?

在《程序员修炼之道》中的 Design by Contract 里提到咱们编写“害羞”的代码是颇有益处的。“害羞”有两个含义:“不要把本身暴露给别人”和“不要与过多的人相互影响”。 这个是什么意思?咱们用书中的例子来理解一下。

在一个庞大的间谍组织中,特工们会分到各个小组,每一个小组内部的特工基本都互相认识,可是各个小组之间的特工就都互不相识。假设某个特工被俘虏了,一个小组可能会被摧毁,可是其余小组的特工是不会被暴露被影响的。由于各个小组之间的关系都是绝对隔离的。可是在任务中,各个小组之间都是会有合做和互相帮助,可是都互不相识。因此这么庞大的间谍组织才能长期安全存活下来。

这个种隔离模式用在编程中是很是好的。把咱们的代码解耦到相对独立的模块和方法中,让它们之间的关联性和影响性降到最低。若是一个模块或者逻辑方法出了问题,咱们能够独立重构或者修复,而不会给其余模块带来巨大的影响。只要最终的结果是一致的,就能够完美优化升级或者修复了。

在程序中,咱们须要一个Service (服务)给咱们处理一个Object(对象),或者请求一个服务得到一个Object,咱们但愿这个服务给到咱们须要的结果,可是不须要咱们去操心它是怎么处理与得到这个Object的。这个服务或者方法是独立运行的,里面的逻辑和代码是与咱们写的代码绝对隔离的。咱们只须要在得到结果的时候验证这个结果的可用性就能够了,若是结果与咱们须要的不一致,那咱们就能够抛出错误。只要这个服务作对应的修正,就能够继续运行了。

理论咱们解说的差很少了,如今咱们来个实战例子吧:

案例: 假设如今咱们须要写一个获取天气预报数据的类,获取天气预报数据首先你须要提供Geolocation 定位信息参数。Geolocation对象中含有一个地址对象。里面有经纬度,省市区等数据。咱们须要获取到地址中的经纬度才能获得精准定点的天气预告信息。咱们的代码会这么写:

/** * 获取天气方法 */
public function getWeather(Geolocation $geolocation) {
	// 假设咱们已经封装了一个获取定位的天气的方法叫getWeatherByGeo()
	return $this->getWeatherByGeo($geolocation->getLocation()->getLat());
}
复制代码
  • 咱们经过getLocation方法获取到定位对象里面的地址对象
  • 而后经过getLat()方法获取到定位地址的经纬度信息

以上例子中,由于咱们须要在geolocation对象中取到经纬度,因此咱们须要先通过获取地址对象,而后再经过这个对象获取到经纬度。其实这里面有不须要的关联关系。不管是写服务,仍是写对象方法,咱们都不要让使用这个服务/对象的开发者去过分的理解和使用你关联性很强的内部方法。这样会致使若是咱们那天改变了这个关联性,多处都须要修改代码。

若是那天刘某改了Geolocation对象,里面再也不含有Location对象,并且也没有了getLocation()方法,经纬度能够直接在Geolocation对象中直接取得。这个时候全部以前运用这个对象的其余人都须要修改代码了。不少时候开发者很难修改代码,或者一改动就会伤筋动骨的,其实就是由于这种过多过分的关联性关系致使而为的。

因此做为Geolocation对象的封装者,咱们应该直接给到一个方法getLat(),让调用这个对象的开发者直接能拿到所须要的信息:

/** * 获取天气方法 */
public function getWeather(Geolocation $geolocation) {
	// 假设咱们已经封装了一个获取定位的天气的方法叫getWeatherByGeo()
	return $this->getWeatherByGeo($geolocation->getLat());
}
复制代码

这样就剪断了刚刚对象中的强关联关系的缺陷。


服务化 --- 「Service」

服务定义:

角色:服务是系统架构里面的业务处理层。 做用:主要是为了高度解耦和封装不一样场景的业务和功能到对应的服务,然而达到高度中心化的业务代码。

理解服务

  • 假设是一个控制器,如今拿到了一个衣服对象参数,而后人拥有一个洗衣服方法
  • 如今人须要洗衣服,可是手洗效率过低了,因此咱们写了一个多功能的洗衣机服务给到人去使用
  • 洗衣机这个服务里面有不少不一样洗衣服的方法,可是其实具体洗衣机里面的每个清洗方法人是不知道怎么实现的,人都是直接按照提供的功能直接使用。
  • 因此服务里面的全部方法都是解耦在服务里面,服务要提供的方法是能够方便人使用的。

这样说是否是很好理解了?因此最简单的理解就是:

服务是用来封装业务逻辑代码,是一个独立的逻辑层,高度封装解耦后提供给控制器或者其余须要用到这个服务的地方使用的。


编写思路

错误例子

把全部洗衣机的方法提供给人使用,那就等同于让人来决定全部洗衣机的参数和清洗步骤。当人放衣服到洗衣机后,要选择先加水,加多少水,而后清洗开始,清洗多久,再甩干等等。

光想一想,洗个衣服还那么多的选项,还要想怎么样的洗衣顺序才是正确的! 我太难了!洗个鸡腿哦!(ノ`□ ´)ノ⌒┻━┻

⭕️ 正确例子

洗衣机服务实现了不少不一样的经常使用洗衣服的模式, 好比快速清洗,毛衣清洗,地毯清洗,风干,甩干等等。都是一些经常使用的功能。 每一个功能方法里面其实调用了不少洗衣机封装好的流程和方法。因此当人使用洗衣机时,根本就不须要知道这些功能是怎么实现的,只要知道本身要干吗,洗衣机恰好也有这个模式,直接用就完事儿了。

(✧ᗜ✧)👍哇! 介么人性化的么!这种洗衣机给我来一打谢谢!

我写过一篇详细关于编写服务的文章《你真的懂怎么写服务层吗?》,有兴趣的童鞋能够前往查看哦。这里我就不详细解说了。

总结

这篇文章已经到达尾声了,到了这里咱们已经深入知道何为易于改编原则,更懂得如何编写易于改编的代码。其实在开发的过程当中,咱们仍是须要先思考,后设计,再编写。根据所拿到的的功能需求,作好程序的架构设计,从而写出易于改编的程序。只有这样咱们编写的代码才能愈来愈好,走上技术巅峰!

相关文章
相关标签/搜索