用“易于改编”原则,提升编程水平,写出更高质量的代码
不管新手仍是资深开发者都会常常问一个问题,“怎样写好的代码?”,要知道怎样写好代码,首要咱们要知道怎样样才是好的代码。要有清晰的目标,才干知道怎样达成目标。在《程序员修炼之道》中说到的“ETC Pri1 ^ 0 % E x vnciple” -- 易于改编准则
。这个准则看似简略,可是咱们越是深化考虑越是觉得“简约F V R h 3而不简略”。
这篇文章里会详细解刨在实践产品研制中“易于改编”的原因和怎样做到“易于改编”, 然后让咱们编写q ! u ^ M @ |出更好的代码。

「一」程序为何需求“易于改编”?

为何代码必需求易于改编?由于一7 % p # B y S !个体系是会跟着一个产品的发展,每日有用户增长就会有一向做不完的需求。只需公司一向在运营着这个产品,需求就会跟着公司的发展而改动。| d 0 t w D V只需咱们开发者一向与时并进专研新技能,咱们就需求一; B r P j S %向晋级优化。
只要了解清楚一个体系在一个生命周期中,详细什么会推进咱们程序改动,从中咱们才会更深化明白为什么咱们的代码需求”易于改编“。
需求会变
不管咱们是研制c # ~ R $ B ` ; 8任何体系,产品需求都是会一向变的。这个是永恒不变的命运。为什么呢?
-
产品方向
— 跟着产品的营销,运营,发展会推进产品需求一向新增,批改,优化。 -
运用量
–( : u h & — 跟着产品的用户量级,数据量3 ] 0 & ^级,并发量级也会推进程序的架构和战略上的改动。 -
技能晋级优化
— 乃至是咱们运用的语言,结构,依靠包等晋. h 1 n = | v级也会引起咱们的代码需求习惯。 -
技能债
— 可能是由于时c 4 R L H T 7 ` }间? A R 8 L o O O的限制,之前的代码重于完成而质量欠安。
所以咱们的代码会t y z跟着年月的流逝一向在迭代晋级优化。
“可快D F c = ? J ^速更o N m V变”是一个软件的中心
近几年许多技能团队启用了灵敏迭代开发
形式。什么是灵敏X A e 1 / 2 {迭代呢?
灵敏迭代便是把开发周期缩短到1-4周。小步快跑的灵敏迭代交给功用$ ? 9上线f & Y。灵敏迭代的流程分别如下:
- 承认需求 – 与老板和市场承认需求和流程
- 需求评审 – 与开发同频需求里` s l y t边的功用点和事务流程
- 技@ # g &能反讲 – 开发与产品u ] N J &同频需求,确保两边了解无误区,开发也需求评价开发难D h * | ?度和开k ] t @ r 5 h发时间
- 研制周期 – 开发人员开始投入研制直接到功用和需求开发完毕,转交给测验,在测验q Y [ + ` D g s环境提测
- 测验周期 – 测验和开发人员开始扫除缺点,批改一切在开发进程产生的bug
- 检j f I ; $ ) ) 7验/预发布周期 – 当测验在测验环境把一切bug扫除去后,当时迭h 1 m代版别就会发布到预发布环境让市场和产品检验功用
- 发布正式 – 当检验经过后,当时迭代版别就能够布置上线到N ! v G , } 1 {正式环境
- 正式回归测验 – 发布上线后,就会有正式回归测验,终x x 9 – t v ` /究一道防地,确保体系参加的一切新功用都无问题
- 迭代总结 – 每一期迭代结束后都总结? S Z | D } 2 这次迭代遇到的问题,持续优化,提高功率
你想想假如一个APP或许体系,几个月乃至一年才更新一次功用和晋级。咱们用起来其实很单调的,乃至咱们会发现许多问题,还有许多功用能够快捷或许提升咱们的运用体验。可是这么久才更新一次,咱们还会对这个产品抱有期望吗?(除了微信这种现Y ! B | S已很老练的应用,可是就算是微信也是有持续更新的)。
所以一个好的产品,是需求快速迭代,小步快跑的灵敏迭代交给{ | 3 z i功用上线的。也是由于这样,功用就需求持续更新、晋级和优化。自然咱们研制的代码就需求一向跟着产品的改动而改编。并且仍是每1-4周就会晋级优化一次。
小总结一下:
- 一个体系会跟着产品的发展和迭代,一向走在改动和更新的道路上。
- 由于系共同向在变,代2 / 2 c 5 ` o ` M码就需求呼应体系的改动,持续的快速迭代晋级优化。
- 已然代码需求G c H d % C s V h快速的更变2 ) 3 { b k和晋级,那程序的“易于改编”性就必需求k i O R y高。
「二」怎样做到“易于改编”?

咱们深化懂得为什么体系会一向在改动,那咱们就要知道怎样写代码才干让一个程序“易于* E q ^ s ~ h 4 +改编”,可是在灵敏迭代中才干快速的呼应需求的改动。假如想让咱们编写的程序更容易的呼应需求改动、事务改动和逻辑改动等,咱们就要充沛的给咱S ~ , Y m z们的程序解刨逻辑。
说到逻辑与事务的分化,首要要根据需求和功用深化考虑剖析,然后对其进行一个架构的规划。最常用的办法便是把体系模块化,组件化等的体系架构规划。
模块规划 —「Modular Design」
模块规划,便是以功用块为单位进行程序规划,完成其求解算法的办法称为模块化。模块化的意图是为了下降程序杂乱度,使程序规划、调试和保护1 | a . b等操作简略化。
不论是前端开发仍是后端开发,咱们都有模块化和组件规划形式。运用模块规划来分化咱们的功用和逻辑,意图是为了下降程序的杂乱度、利于调试、保护、批改和新增功用。
比方现在咱们要做个CMS(内容办理体系),咱们一起来尝试运用模块规划来分化这个体系的功用。
规划思路
首要咱们Z p $ | p C 5要了解一个内容办理体系有哪些功用,然后把每个功用划入各个模块里。可是许多童鞋一开始接触一个体系,然后开始瓜分模块会觉得无从入手,可能花了半响坐在电脑前考虑,可是半响都吐不出一个所以然来。接下来让咱们一起来学习一套逻辑思维,让咱们今后更轻松架构一套模块规划吧!
一开始先考虑这个体系的意图和运用场景,这个体系是用来做什么的?
一T + ; A u k个内容办理体系,一般来说都是用来发发文章,新闻,或许是一个官方网站的内容办理。那必定就有文章。那办理文章内容,需求什么功用呢?
文章模块 「Article 模块」
- 增删查改文章
- 文章草稿
- 文章置顶
文章子模块 — 分Y p n类 「Article Category 模块」
- 增删查改分类
- 文章图片
那这些与文章相关的功用是不是能够共同放在“Article( : n”
模块中共同办理,然后文章的模块中还有一个文章分类的子模块
叫做“Catege 5 }ory”
。
有文章了必定就需求有作者,那作者在体系中其实是一个用户。那咱们就需求有用户模块了。
加上c & p u M F X 8 V一个` T G @ A r G g办理体系,必定就有办理员,作者,* I / $ s T , ]乃至是会员。走一波这个逻辑咱们就发现应该要有以下的功用点。
用户模块 「User 模块」
- 用户增删查改
- 用户身份办理
- 用户权限办理
- 会员等级办理
这么一v Q K R来咱们就能够建立一个单独的User模块
。这个模块首要是办理用户相关的信息和功用。
看到这儿咱们应该对一个体系的模块构思有一点的 c U ` P X B概念了。这个时分产. i F ,品司理过来给咱们提了一个需求,“咱们现9 a V g在要在这个+ P w 8 ] X z ; Z体系添加一个标签体系,专门用来办理文章标签的。”。
那童鞋们,你们觉得这个需求应该放入那个模块呢?
….
你们答对了!这个是属于文章的一个子模块,Tag模块
— 专门Q a ) , ) (办理文章的标签,然后和每一篇文章有多对多联系的。所以标签模块
归纳入文章模块中。假如咱们的内容办理体系做的很大,里边2 $ 1 r ^ [ x H有视频内容,图文文章0 W a u等等。咱们能够在一开始就把这些共同归纳入L a ` 5 2“内容模块”? P } k w K,也便是Content
模块中。
前端模块规划
说到了这儿前端的童鞋估量要举手咯♂️,前端的咱们求关注呀!“前端是以页面和交互为单位,不可能和后端相同按功用逻辑来分化模块吧?” — 这个童鞋说的有理哈。其实前端和后端的规划上是有稍微的不相同的。
后端会以事务逻辑来分化模块,可是前端有页面和数据逻辑两块的代码。所以前端相对比后端就要分开两种模块分化思路了。
页 (排) 面 (版) 的模块规划
- 前端的页面模块与产品定义的体系模块会愈加恰当一M ] I 4 z些。前端分化的模块会跟用户所看到的操作功用分组。
- 简略的模块分化,能够利用产品童鞋给到咱们的导航来分化,这样会更合理的规整咱们的页面模块。
- 假如在页面功用上再想细分,那就能够用
组件规划
来分化了。
前端逻6 h J O辑模块规划
-
几年前的前端便是个“切图仔”,底I { Z | Q子不必考虑什么事务逻辑,数据逻辑,数据交互这些技能领域。可是由于前后端分离现在现已变成大多数公司的研制战略。渐渐前后端都各自分摊了事务逻辑和数据交互等处理。
-
由于前端也有许多的事务逻辑和交互逻辑,所以在咱们封装宽和耦的时分,也会遇到需求分化模块来处o $ X ; C } )理。现在最典型的比如便是在运用
Vue
的状态办理Vuex
的时分,需求用到模块办理
来分化逻辑,使后边保护和批改更容易。 -
其实前端C * ^ M e j也是用后端同一套思维形式来分化事务就能够了,以功用为单位来L W i 9 y P分化你们的模块就能够了。
解耦 – 「Decoupling」
解耦,便是把杂A t ; = O ; l 8乱繁琐的逻辑拆分红更小的逻辑块。然后让杂乱的逻辑分化成小的逻W { a Y辑处理,使得逻辑变得更简化,更易E : A于调试和保护。
在一个功用众多、事务杂乱和体系模块繁复的体系中,每一个模块里边的代码也会开始变得臃M p @ c n肿,越来越难调试、保护和办理。其实模块化宽和耦是共同的。模块化也是为了解耦你的程序。这儿咱们要点讲的是模块之间和逻辑之间的解耦(Decouping)。
我共享一个阅历让咱们深化认知到解耦的重要性。我遇到过最夸张的有一段逻辑处理写了上5000行代码的童鞋,可是更可怕的是,在相同功用的当地那5V m 3 ( a000行代码被复制粘贴过来了。我滴乖乖,这位童鞋在研制小组中有个花名叫“复制兄”。不过得到b N X 0 l 9咱们的帮助和提点下,后边他也成为了这个小组中的一名优秀的程序员。
假如咱们不B I D懂得解耦代码,编写的代码会给咱们后边带来很重的“技能债”。假定一下,你的5000行处理逻辑,在上数$ X p 1 d y N十个当F r N地运用了。咱们要改一下这段逻辑就难过登天了。就算是这段逻辑没有复用性,但当你需求回头去批改这段逻辑也是会让你头皮发麻,无从入手。批改一点# w ] z _这个逻辑都可能会导致呈现10个bug的后果。
咱们深化知道解耦的重要性,那; o u P h么咱们应该怎e 6 B n U n样去高效解耦代码呢?
在《程序员修炼之道》中的 Design by Contract
里说到咱们编写“害臊”的代码是很有益处的。“害臊”有两个含义:“不要把自己暴露给他人”和“不要与过多的人相互影响”。 这个是什么意思?咱们用书中的比如来了解一下。
在一个巨大的间谍安排中,奸细们会分到各个小组,每个小组内部的奸细底子都互相知道,可是各个小组之间的奸细就都互不相识。假定某个奸细被俘虏了,一个小组可能会被b K t摧毁,可是其他小组的奸细是不会被暴Z W m @ u z h J w露被影响的。由于各个小组之间的联系都是肯定阻隔的。可是在使命中,各个小组之间都是会有合作和互相帮助,可是S ~ , T c +都互不相识。所以这么巨大的间谍安排才干长时间安全存活下来。
这个种阻隔形式用在编程中是非常好的。把咱们的代码解耦z c W } m A ` d到相对独立的模块D 0 ~ c D v和办法中,让C a s它们之间的相关性和% , d : ! : S影响性降到最低。假如一个模U F j块或许逻辑办法出了问题,咱们$ z ]能够独立重构或许批改,而不会给其他模块带来巨大的影响。只y 8 u D 1 j s需终究的成果8 y | $是共同的,就能够完美优化晋级或许批改了。
在程序中,咱们需求一个s 9 ` I Q DService (服务)
给咱们处理一个Object(目标)
,或许请求一个服务获得一个O| # Abject
,咱们期望这: Q * Z B &个服务给到咱们需求的成果,可是不需求咱们去操心它是怎样处理与获得这个Object
的。这个服务或许办法是独立运转的,里边a H J 9 o的逻辑和代码是与咱们写的代码肯定阻隔的。咱们只需求在获得成果的时分验证这个成H ; & k Z F果的可用性就能够了,假如成果与咱们需求的不共同,那咱们就能够抛出过错。只需这个服务做对z R 1 !应的批改,就能够持V A n : – ^ Y x .续运转了。
理论咱们说明的差不多了,现在咱们来个实战比如吧:
事例:
假定现在咱们需求写一个获取气候预报数据的类,获取气候预报数据首要你需求供给Geolocation 定位信息
参数。GH : q = o Y _eolocation
目标中含有一个地址目标。里边有经纬度,省市区等数据。咱们需求获取到地址中的经纬度才干得到精准定点的气候预告信息。咱们的代码会这么写:
/**
* 获取气候办法
*/
public function getWeather(Geolocation $gek - d aolocation) {
// 假定咱们现已封装了一个获取定位的气候的办法叫getWeatherByGeo()
return $this->getWeatherByGeo($geolocation->getLocation()->getLat());
}
- 咱– } B 0 x E ? k们经过
getLocation
办法获取到定位目标里边的地址目标 - 然后经过+ : 6 [
getLat()
办法获取到定位地址的经纬度信息
以上比如中,由于咱们需求在geolocation
目标中取x , h到经纬度,所以咱们需求先经过获取地址目标,然后再经过这个目标获取到经纬度。其实这儿面有不需求的相相关系。不管是写服务,仍是写目标办法,咱m R [ _ ~ H C们都不要让运用这个服务/目标的开发者去过度的了解和运用你相关性很N h 6 Z Y O W强的内部办法。这样会导致假如咱们那天改动了这个相关性,多处都需求批改代码。
假如那天刘某改了Geolocation
目标,里边不再含有Location
目标,并且也没有了getLocatid _ J !on()
办法,经纬度能够直接在Geolocation
目标中直接L _ 5获得。这个时分一切之前运用这个目标的其他人都需求批b S j改代码了。许多时分开发者很难批改代码,或许一/ B a p Q改动就会伤筋动骨的,其实便是由于这种过多过度的相关性联系导致而为的。
所以作为Geolocation
目标的封装者,咱们应该直接给到一个办法getLat()
,让调用这个目标` 9 u ]的开发者直接能拿到所需求的信息:
/**
* 获取气候办法
*/
public function getWeather(Geolocation $geolocation) {
// 假定咱们现已封装了一个获取定位的气候的办法叫getWeatherByGeo()
return $this->getWeat; % [ P cherByGeo($geolocation-&W K 1 ; W cgt;getLat());
}
这样就剪断了刚刚目标中的强相相关系的缺点。
服务化 — 「Service」
服务定义:
人物
:服务是体系架构里边的事务处理层。
作用
:首要是为了高度解耦和B P @ h V J封装不同场景的事务和功用到对应的服务,可是达到高度中心化的事务代码。
了解服务
- 假定
人
是一个控制器
,现在拿k I z 0 ] X w r 0到了一个衣服目标
的参数
,然后人具有一个洗衣服
的办法
- 现在人需求洗衣服,可是手洗功率太低了,所以咱们写了一个多功用的
洗衣机服务
给到人去运用 -
洗衣机
这个服/ Y ] ) z j务里边有许多不同洗衣% O X B y & 4 . s服的办法
,可是其实详细洗衣机里边的每一个清洗办法人是不知道怎样完成的,人都是直接依照供给的功用直接运用。 - 所以服务里边的一切办法都是解耦在服务里边,服务要供给的办法是能r e b够方便人运用的。
这样说是不是! * g b 1 n x /很好了解了?所以最简略的了解便是:
服务是用来封装事务逻辑代码,是一个独立的逻辑层,高度封装解耦后供给给控制器或许其他需求用到这个服务的当地G n P运用的。
编写思路
❌ 过错比如
把一切洗衣机的办法供Y E Y 2 G / 7 .给给人运用,那就等同于让人来决定一切洗衣 r [机的参数和清洗步骤6 % w。当人放衣服到洗衣机后,要选择先加水,加多少水,然后清洗开始,H , A ^ {清洗多久,再甩X . ) Z 2 b 7干等等。
光想想,洗个衣服还那么多的选项,还要想怎样样的洗衣顺序才是正确的! 我y Y C D太难了!洗个@ p ; (鸡腿哦!(ノ`□ )ノ⌒┻━┻
⭕️ 正确比如
洗衣机服务完成了许多不同的常用洗衣服的
形] 6 b式
, 比方快速清洗,毛衣清洗,地毯清洗,风干,甩干等等。都是一些常用的功用。
每个功用办法里边其实调用了许多洗衣机封装好的@ C G / X ? E ,流程和办法。所以当人运用洗衣机时,底子就不需求知道这些功用是怎样完成的,只需知道自己要干嘛,洗衣机刚好也有这个形式,直接用就完事儿了。
(✧ᗜ✧)哇! 介么人性化的么!这种洗衣机给我6 M F = l / U w来一打谢谢!
我写过一篇详细关于编写服务的文章《你真的懂怎样写服务层吗?》,有兴趣的童鞋能够前往检查哦。这儿我就不详细说明了。

总结

这篇文章现已到达尾声了,到了这儿咱们现已深化知道何为易于改编
准则,更懂得怎样编写易于改编
的代码。其实在开发的进程中,咱$ W Y们仍2 V ^是需求先考虑,后规划,再编写。根据所拿到的的功用需求,做好程序Z G @ i F O 1的架构规划,然后写出易R f @于改编的程序。只要这样咱们编写的代码才干越来越好,走上技能巅峰!
