1.2 面向对象范型之前:功能分解
设计模式解析(第2版•修订版)
功能分解是一种处理复杂问题的自然方法
让我们从对一种常用的软件开发方法的考察开始吧。如果给你一个任务,要编写一段代码,访问在数据库中存储的形状描述然后显示出来。按照所需要的步骤来思考,是一种很自然的选择。比如,你可能认为应该按照以下步骤解决这个问题。
1.在数据库中找到形状列表。
2.打开形状列表。
3.按某种规则将列表排序。
4.在显示器上显示各个形状。
还可以选取以上任意一个步骤,进一步分解成实现所必需的若干步。例如,可以将步骤4分解。对于列表中所有形状,都可以按照以下步骤进行。
4a. 识别形状的类型。
4b. 获取形状的位置。
4c. 以形状的位置作为参数,调用显示形状的函数。
这种方法就称为“功能分解”,因为分析人员将问题分解成了多个功能步骤(这些步骤就构成了这个问题)。你我都会这样做,因为解决更小的问题,比解决整个问题更简单。这种方法与我写制作意大利肉末番茄烤面条的烹饪过程,或者装配自行车指南所用的方法是一样的。这种方法我们使用得如此驾轻就熟,以至于我们很少对它有所怀疑,或者自问是否还有其他的选择。
这种方法的挑战:能者多责
功能分解方法的一个问题在于,它通常会导致让一个“主”程序负责控制子程序,这是将功能分解为多个子功能的自然结果。但是,主程序所承受的责任太多了:要确保一切正确工作,还要协调各函数并控制它们的先后顺序,因此经常会产生非常复杂的代码。如果让一些子函数负责自己的行为,而且能够告知主函数执行某些任务,并信任它知道如何执行,这种方式比功能分解的方式要容易得多。叱咤疆场的将军和家庭中成功的父母对这种经验都了然于胸。现在,程序员也学会了,这就是所谓委托(delegation)。
这种方法的难题:应对变化
功能分解方法的另一个问题在于,它在为未来可能出现的变化未雨绸缪方面,在对代码合适地改进方面,都于事无补。变化是无法避免的,经常是因为要为已有的主题增加新的变体。例如,我可能不得不处理新的形状,或者需要显示形状的新方法。如果将实现各步骤的所有逻辑代码都放在一个大函数或大模块中的话,那么这些步骤的任何实质性变化,都必须对这个函数或模块进行修改。
而且变化还会为bug和意料之外的结果创造机会。或者,像我喜欢说的:
许多bug都源于代码修改。
自己去验证这句断言吧。考虑这样的情景:想对代码进行修改,但又害怕这样做,因为你知道修改一个地方的代码可能会破坏其他地方。怎么会出现这种情形呢?代码非要关注所有函数和使用它们的方式吗?函数应该怎样和另一个函数交互呢?函数要关注的细节是否太多了,比如要实现的逻辑、要交互的东西、要使用的数据?和人一样,如果程序试图同时关注过多的东西,一旦有变化出现,就只能坐等bug来到。程序设计可是一种复杂、抽象和动态的活动啊。
而且,无论多么努力工作,无论分析做得多么好,也是永远无法从用户那里获得所有需求的,因为关于未来有太多未知,万物皆变化。不是吗,它们总是在变化之中……
对于阻止变化,我们无计可施。但是我们对变化本身却并非无能为力。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。