一、面向对象泛型之前:功能分解

  功能分解是一种处理复杂问题的自然方法。

    举个简单的例子:编写一段代码实现去图书馆借书的功能。

    1.在电脑上检索你需要的图书籍。

    2.到图书馆找见你需要借的书籍。

    3.借书。

    对于上面的3步,还可以选择任意一个步骤,进一步分解成实现所必需的若干步,例如,可以将步骤3进行分解。

    3a.将待借的书籍放在自动借书机器上。

    3b.识别所借的书籍。

    3c.刷手机(借书卡)识别借阅者信息。

    3d.将借阅记录保存起来。

    3e.取走书籍。

    这种方法就成为功能分解法,分析人员将问题分解成了多个功能步骤。你我都会这样做,因为解决更小的问题比解决整个问题更简单。

    功能分解法的一个问题在于:它通常会导致让一个主程序负责控制子程序,这是将功能分解为多个子功能的必然结果。但是主程序负责的责任太多了:要确保一切正常工作,还要协调各函数并控制它们的先后顺序,也正是因此,经常会产生非常复杂的代码。

    如果让一个子函数负责自己的行为,而且能够告知主函数执行某些任务,兵新人它知道如何执行,这种方式比功能分解的方式要容易的多。叱咤疆场的将军和家庭中成功的父母对这种经验都了然于胸。现在,程序员也学会了,这就是所谓的委托(delegation)

    功能分解的另一个问题在于它在为未来可能出现的变化未雨绸缪方面,在对代码合适地改进方面,都于事无补。变化是无法避免的,经常是因为要为已有的主题增加新的变体。如果将实现各个步骤的所有代码都放在一个打函数或者大模块中的话,那么这些步骤的任何实质性变化,都必须对这个函数或者模块进行修改。

    考虑这样的情景:相对代码进行修改,但又害怕这样做,因为你知道修改一个地方的代码可能会破坏其他地方。怎么会出现这种情形呢?代码非要关注所有的函数和使用它们的方式吗?函数应该怎样和另一个函数交互呢?函数要关注的细节是否太多了,比如要实现的逻辑、要交互的东西、要使用的数据?和人一样,如果程序试图同时关注过多的东西,一旦有变化出现,就只能坐等bug的到来。程序设计可是一种复杂、抽象和动态的活动啊。

    而且无论多么努力的工作,无论分析做的多么好,也是永远无法从用户那里获得所有需求的,因为关于未来有太多未知,万物接变化,不是吗,它们总是在变化之中......

    对于组织变化我们无计可施,但是我们队变化本身却并非无能为力。

二、需求问题   

 需求之所以变化,有如下几个简单原因:

    1.用户对自己需求的看法,会因为与开发人员的讨论以及看到软件新的可能性而发生变化。

    2.开发人员对用户领域的看法,会在开发使用该领域自动化的软件的过程中,因为对它更加熟悉而发生变化。

三、应对变化:使用功能分解存在的问题

    功能分解法中模块化虽然有助于代码的可理解性,而容易理解使得代码更容易维护,但模块化并不是始终有助于代码应对所有可能遇到的变化。

    问题:低内聚(weak cohesion)和紧耦合(tight coupling)

    首先看什么是内聚性和耦合性:

    内聚性:例程(或类)中操作之间关系的紧密程度。说一个类低内聚,指的就是它任务很多而且互不相关,代码经常看上去像是令人疑惑的一大团。在极端的情况下,这些类会与系统中差不多所有的东西纠缠在一起,据说有人称之为“上帝对象”,因为他们好像是万能的(或许只有上帝才可以理解它们)。

    耦合性:两个例程之间关系的紧密程度。耦合性与内聚性是相辅相成的关系。内聚性描述的是一个例程内部组成部分之间的紧密程度。而耦合性是一个例程与其他例程之间联系的紧密程度。目标应该是创建这样的例程:内部完成(高内聚),而与其他例程之间的联系则是小巧、直接、课件、灵活的(松耦合)。

    大多数程序员都会有这样的经验:在代码的某个地方修改了一个函数或一个数据,后来却对代码的其他地方造成了意想不到的影响,这种bug成为“不良副作用”。这是因为,虽然我们获得了希望的结果,但是也得到了不需要的结果----bug!更糟糕的是,这些bug经常难以发现,因为我们一开始旺旺不会注意到那些导致副作用的代码联系(如果刚开始能够注意到这些联系,就不会用这种方式修改程序了)。

    事实上,这种bug使我有了一个非常惊人的发现:我们实际上并没有花费很多时间改正程序的bug。

    我认为,在维护和调试过程中,改正bug只需要花费很少的时间。维护和调试的绝大多数时间都被用在努力弄清代码的运作机理、寻找bug和防止楚翔不良副作用身上了,真正的改正时间却相当短!