扫码阅读
手机扫码阅读

内功修炼心法 之 整洁的代码维护

283 2023-08-03

前面我们谈到了快速交付的重要性,然而怎样才能提高团队的交付速度呢?今天我们来谈一谈快速交付团队内功修炼心法之——整洁的代码维护

 

软件开发就像一个管道,软件开发中的每个环节的速度都会影响团队整个的交付速度。而在整个软件开发的流程中,代码的编写无疑是占比最大的一个环节。同时,随着软件业的不断发展,未来的发展趋势是每个软件的生命周期都会越来越长,就意味着,今后我们更多的时间是在更新维护软件,而不是开发新的软件。但是,如何快速高效地去更新维护一套软件,成为了所有软件团队的梦魇。

 

所有软件发展的轮回就是,随着业务的不断开展,用户会对软件提出越来越多的需求,使得软件规划越来越大,软件代码越来越复杂,从而导致软件维护越来越困难,更新周期越来越长。如果在日后的软件维护过程中,不能保证软件的设计质量,有效降低软件的规模与复杂度,那么软件维护就会变得越来越难,周期越来越长,就毫无快速交付可言了。怎样才能保证软件的设计质量呢?简单的说教与规范管理收效甚微。我认为,要真正交给开发人员一套切实可行的方法,让开发人员在开发过程中有迹可循,才是解决之道。而在面对复杂业务应用时,这个方法就是“领域驱动设计”。

 

 

比如,如图是电商软件的付款功能,最初的需求是比较简单、清晰、明了的,因此我们最初的代码设计也是比较清晰明了的。接着,软件开始各种变更。首先的需求变更是增加商品折扣,而商品折扣还分为限时折扣、限量折扣、某类商品折扣与某个商品折扣。为了实现这个需求,我们在这段代码中进行扩展,增加了一个if语句,if是限时折扣就怎么样,if是限量折扣就怎么样……这时,代码就开始逐渐膨胀。在这样的基础上,又不断在增加新得需求:VIP会员、支付方式。每次变更,都在这个payoff方法中不断地塞代码,代码质量变得越来越差。随后,各种秒杀、预订、闪购、众筹,甚至返券功能的添加,这段代码就越来越乱,变更也越来越难。许许多多的软件就是在这样的变更中不断轮回,这是我们不愿看到的。

 

如何摆脱这样的轮回呢?我们的设计必须做出改变,那就是基于领域的分析设计。软件的本质就是对真实世界的模拟,因此领域驱动设计的基本思想就是将软件设计与真实世界对应起来。真实世界是什么样子,软件就怎样设计。这样的对应体现在以下三个方面:1)真实世界有什么事物,软件世界就有什么对象;2)真实世界中这些事物有什么行为,软件世界中这些对象就有什么方法;3)真实世界中这些事物之间有什么关系,软件世界中这些对象之间就有什么关联。我们就是将我们对业务的理解,按照这三个对应绘制成了领域模型,然后再根据领域模型指导软件开发的。

 

 

当软件变更时,我们必须在原有的代码上进行修改,又不希望影响原有的功能。正因为如此,我们采用了“使代码修改最小化”的设计思想,即不修改原有的代码结构,而是在原有代码的基础上塞代码。这样导致的后果就如该案例一样,payoff方法不断膨胀,变得越来越复杂而难于阅读、难于维护。这就是代码退化的根源,它不是哪个设计原则、代码规范、质量管理可以阻止的。

 

怎样才能阻止代码退化呢?我们必须得明白软件发展的必然规律。软件是对真实世界的模拟,但真实世界又是十分复杂的,因此人认识真实世界总是有一个由简至繁的过程。最初的第一个版本,我们将真实世界中那些简单、明了、易于理解的需求识别出来,做到了软件里。然而,当软件上线以后,客户却发现还有许多不简单、不明了、不易于理解的需求没在软件里,使他们使用软件不方便。因此,他们就会提BUG,提新需求。我们在不断修复BUG,实现新需求的过程中,软件越来越接近真实世界,使客户越来越爱用我们的软件。然而,这个过程却使得软件规模越来越大,代码越来越多,越来越复杂。

 

简单软件有简单软件的设计,复杂软件有复杂软件的设计。当软件不断地由简单软件向复杂软件转变时,我们必须要调整程序结构来适应越来越复杂的业务逻辑。这个过程就好像生产力与生产关系一样,当生产力发展了就一定要调整生产关系,当业务逻辑复杂了就一定要调整程序结构。如果不调整程序结构,而是在原有的程序结构上塞代码,那么代码就必然会退化,代码质量就越来越差,日后的变更就必然会越来越困难。这就是代码退化的根源,是任何一个代码管理都无法阻止的。唯一的解决方法就是在每次需求变更时正确地设计,运用“两顶帽子”的方法,先调整程序结构以适应新需求,然后再实现新的需求。只有这样才能保持代码质量不退化,使团队保持低成本运维一个系统。

 

但是,理论是这样,在项目实践中,当新需求到来时,应当怎样调整程序结构以适应新的需求呢?这个事情可能在第一次变更、第二次变更、第三次变更时还想得清楚,但在第10次变更、第20次变更、第30次变更时就可能想不清楚了,设计开始迷失方向。怎样才能不迷失方向呢?那就是领域驱动设计,即将变更还原到真实世界中,然后根据真实世界去指导领域模型变更,然后将领域模型的变更映射成程序变更,我们的设计质量就提高了。

 

比如,刚才那个商品折扣的需求,我们在变更时首先回到领域模型中进行分析设计。这时,我们要分析付款与折扣之间的关系。付款与折扣是什么关系呢?我们应当怎样进行分析设计呢?这时需要回答的是两个问题:1)当付款发生变更时折扣是否一定变;2)当折扣发生变更时付款是否一定变?如果回答都是否定,则说明付款与折扣是软件变化的两个不同的原因。

 

单一职责原则要求软件设计的每个元素都只完成自己职责范围内的事,而这里的关键是对“职责”的理解。过去,我们错误地将“职责”理解为做某个事情,所有跟这个事情相关的操作都是它的职责。正因为这样的理解,我们错误地将“折扣”写到了“付款”中,就会带来诸多的设计问题。怎样正确理解“职责”呢?一个职责就是软件变化的一个原因。

 

怎样解读“一个职责就是软件变化的一个原因”?我们说什么是高质量的代码?在需求变更时维护成本最低的才是高质量的代码。如果一个需求变更需要修改5个模块的代码,每个模块修改完了还要测试,代码维护成本是高,那么怎么才能维护成本最低呢?只改一个地方则成本最低。怎样才能在每次变更的时候都能做到只改一个地方就完成变更呢?就要求我们在平时就不断地整理代码,让因同一个原因而变更的代码放在一起,而让因不同原因而变更的代码分开放,放在不同的模块、不同的类中。

 

正因为这样的理解,我们在设计软件时,就应当将软件变化同一个原因的代码放在一起,而将软件变化不同原因的代码分开放(放在不同模块或不同的类中)。如果我们这样设计了代码,则日后变更时,因为这个原因而需要修改的代码都在这个模块或这个类中,与其它模块或类无关,则代码维护的成本就降低了,代码质量就上升了。基于这样的分析,付款与折扣是软件变化的两个不同的原因,因此,将折扣从付款中抽取出来单独编写一个类。

 

同样,不同类型的折扣,当限时折扣发生变更时限量折扣不一定变,限量折扣发生变更时某类商品折扣也不一定变,这说明不同类型的折扣各自也是不同的软件变化的原因,也应当放到不同的类中。最后的领域模型设计如图:

 

 

基于这样的设计,当付款发生变更的时候与折扣无关,折扣变更的时候也与付款无关,变更范围就缩小了,设计质量就提高了。同样,当限时折扣变更时就修改限时折扣,当限量折扣时就修改限量折扣,与其它类型的折扣无关,维护成本就减小了。如果开发团队每次变更都能这样变更,那么日后的变更成本就会减小,则快速交付将成为可能。同时,软件开发也可以进入一种良性循环,不断地在低成本的状态下维护下去。

 

原文链接: http://mp.weixin.qq.com/s?__biz=Mzg5NDE1OTgyNA==&mid=2247483677&idx=1&sn=c8c3ea7f0f5ca071c9fd74dbbe768da7&chksm=c0229fa2f75516b46fcb141adf70fa555d23d8c0afd5646eb3533b5e43597dc5c49038132f71#rd