本节书摘来自异步社区《设计模式解析(第2版•修订版)》一书中的第1章,第1.4节应对变化:使用功能分解,作者【美】Alan Shalloway(艾伦•沙洛维) , James R.Trott(詹姆斯•R.特罗特),更多章节内容可以访问云栖社区“异步社区”公众号查看。
1.4 应对变化:使用功能分解
设计模式解析(第2版•修订版)
用模块化封装变化
更进一步地来看“显示形状”问题。怎样编写代码才能更容易地应付多变的需求呢?与其编写一个大函数,不如使之更加模块化。
例如,在前面提到的步骤4c“以形状的位置作为参数,调用显示形状的函数”中,可以写一个例1-1所示的模块。
例1-1 用模块化封装变化
函数:显示形状
输入:形状类型,形状描述
操作:
switch (形状类型)
case 方形:调用显示方形的函数
case 圆形:调用显示圆形的函数
功能分解方法中模块化的问题
然后,在接到一个需求,要显示新的形状(例如三角形)时,我只需改变这个模块即可。(希望如此!)
但是这种方法仍然存在问题。比如,前面说过,这个模块的输入是形状的类型和描述。然而,在不同的形状存储方式下,对所有形状都适用的一致描述可能存在,也可能不存在。如果形状的描述有时以包含坐标点的数组方式存储,有时以其他方式存储,怎么办呢?这种方法还适用吗?
模块化肯定有助于提供代码的可理解性,而容易理解将使代码更容易维护。但是模块化并不总是有助于代码应对所有可能遇到的变化。
低内聚,紧耦合
这种方法我一直在使用,我发现它主要有两个问题,按术语来说就是低内聚(weak cohesion)和紧耦合(tight coupling)。在Code Complete一书(Microsoft Press, 1993)中,Steve McConnell对内聚性和耦合性有很精彩的描述。他说:
内聚性(cohesion)指的是“例程中操作之间联系的紧密程度”1。
我还听说过有人将内聚性称为清晰性(clarity),因为例程(或类)中的多个操作联系越紧密,就越容易理解其含义。说一个类低内聚,指的就是它任务很多而且互不相关,代码经常看上去像是令人疑惑的一大团。最极端的情形下,这些类会与系统中差不多所有东西都纠缠在一起,据说有人称之为“上帝对象”,因为它们好像是万能的(或许是因为只有上帝才能理解它们)。
耦合性(coupling)指的是“两个例程之间联系的紧密程度。耦合性与内聚性是相辅相成的关系。内聚性描述的是一个例程内部组成部分之间相互联系的紧密程度,而耦合性描述的是一个例程与其他例程之间联系的紧密程度。软件开发的目标应该是创建这样的例程:内部完整(高内聚),而与其他例程之间的联系则是小巧、直接、可见、灵活的(松耦合)。” 2
修改一个函数甚至是函数所用的数据,都可能对其他函数产生严重破坏
大多数程序员都会有这样的经验:在代码的某个地方修改了一个函数或一个数据,后来却对代码的其他地方造成了意想不到的影响,这种bug称为“不良副作用”。这是因为,虽然我们获得了希望的结果(进行了修改),但是也得到了不需要的结果——bug!更糟糕的是,这些bug经常难以发现,因为我们一开始往往不会注意到那些导致副作用的代码联系(如果能够注意到这些联系,就不会用这种方式修改程序了)。
事实上,这种bug使我有了一个非常惊人的发现:我们实际上并没有花费很多时间改正程序的bug。
我认为,在维护和调试过程中,改正bug只需要花费很少的时间。维护和调试的绝大多数时间都被用于努力弄清代码的运作机理、寻找bug和防止出现不良副作用上了,真正的改正时间却相当短!
因为不良副作用经常是最难发现的bug,所以如果让一个函数处理很多不同的数据,一旦需求发生变化,就更可能出问题。
问题就出在副作用中
只关注函数,就可能引起难以发现的副作用。
维护和调试中所耗费的大多数时间不是花在修改bug上,而是花在寻找bug,弄清如何避免在修改代码时导致不良副作用上了。
功能分解将注意力放在错误的地方
使用功能分解时,需求变更会对软件开发和维护工作产生极大影响。这时候,主要的精力都放在函数上了,而对一组函数或者数据的修改会影响到其他函数和数据,并依此类推地影响到其他必须修改的函数。就像一个雪球滚下山来,一路裹挟了更多的雪一样,只关注函数,将导致一连串变化,而且难以避免。
1 McConnell S.,Code Complete: A Practical Handbook of Software Construction,Redmond: Microsoft Press,1993,p. 81。(请注意,这些术语并不是McConnell发明的,发明者是Ed Yourdon 和 Constantine。我们只是碰巧更喜欢McConnell的定义而已。)
2同上,第87页。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。