1.9 分析和设计

面向对象范式是一种新的、不同的编程思考方式,许多人一开始在学习如何处理一个OOP项目时都会感到非常困难。但是了解到任何事物都被认为是对象,并且学会用面向对象的风格去进一步思考之后,我们就可以开始利用OOP所提供的所有优点创造出“好的”设计。

方法(method)[通常称为方法论(methodology)]是一系列的过程和探索,用以降低程序设计问题的复杂性。自从面向对象程序设计出现,已有许多OOP方法被提出来。本节将让读者感受使用一种方法时应完成什么任务?

尤其在OOP中,方法论是一个充满实验的领域,因此在我们考虑采用一个方法之前,理解它试图要解决什么问题是重要的。这在C++中尤其正确,这种编程语言在表达一个程序时试图减少复杂性(同C相比)。这在实际上可能减缓对更复杂方法论的需求。相反,简单的方法可以满足在C++中处理更大类的问题,在过程型语言中用简单方法处理的问题相比起来则小很多。

认识到术语“方法论”通常太大且承诺太多也是很重要的。设计和编写一个程序时,我们所做的一切就是一个方法。它可能是我们自创的方法,我们可能没有意识到正在创造一种方法,但它确实是我们创造时经历的一个过程。如果它是一个有效的过程,只需要略加调整以和C++配合。如果我们对自己的效率和程序生产方式不满意,就可以考虑采纳一个正式的方法或在许多正式方法中选择某些部分。

经历开发过程时,最重要的问题是:不要迷路。这很容易做到。大部分分析和设计方法都是为了解决最大的一些问题。记住,大多数项目并不适合这一点,因此我们通常可以用一个相对小的子集成功地进行分析和设计[1]。但是采用某种过程,不论它怎么有局限,总比一上来就直接编码好得多。

在开发过程中很容易受阻,陷入“分析瘫痪状态”,这种状态中往往由于没有弄清当前阶段的所有小细节而感到不能继续了。记住,不论做了多少分析,总有系统的一些问题直到设计时才暴露出来,并且更多的问题是到编程或直到程序完成运行时才出现。因此,迅速进行分析和设计并对提出的系统执行测试是相当重要的。

这个问题值得强调。因为我们在过程型语言上的历史经验,一个项目组希望在进入设计和实现之前认真处理和理解每个细节,这是值得赞扬的。的确,在构造DBMS时,需要彻底理解用户的需要。但是DBMS属于能很好表述和充分理解的一类问题。在许多这种程序中,数据库结构就主要是问题之所在。本章讨论的编程问题属于所谓“不定(wild card)”(本人的术语)类型,这种问题的解决方法不是将众所周知的解决方案简单地重组,而是包含一个或多个“不定要素”—先前没有较了解的解决方案的要素,为此,需要研究[2]。由于在分析阶段没有充分的信息去解决这类问题,因此在设计和执行之前试图彻底地分析“不定型”问题会造成分析瘫痪。解决“不定型”问题需要在整个循环中反复,且需要冒风险(这是很有意义的,由于是在试图完成一些新颖的且潜在回报很高的事情)。看起来似乎有风险是由于“匆忙”进入初步实现而引起的,但这样反而能降低风险,因为我们正在较早地确定一个特定的方法对这个问题是不是可行的。产品开发也是一种风险管理。

经常有人提到“建立一个然后丢掉”。在OOP中,我们仍可以将一部分丢掉,然而由于代码被封装成类,在第一次迭代中我们将必然生成一些有用的类设计,并且产生一些不必抛弃的关于系统设计的有价值的思想。因此,在问题的第一次快速遍历中不仅要为下一遍分析、设计及实现产生关键的信息,而且为下一遍建立代码基础。

也就是说,如果我们正在考虑的是一个包含丰富细节而且需要许多步骤和文档的方法学,将很难判断什么时候停止。应当牢记我们正在努力寻找的是什么:

(1)有哪些对象?(如何将项目分成多个组成部分?)

(2)它们的接口是什么?(需要向每个对象发送什么信息?)

只要我们知道了对象和接口,就可以编写程序了。由于各种原因我们可能需要比这些更多的描述和文档,但是我们需要的信息不能比这些更少。

整个过程可以分5个阶段完成,阶段0只是使用一些结构的初始约定。

1.9.1 第0阶段:制定计划

我们必须首先决定在此过程中应当有哪些步骤。这听起来简单(事实上,所有听起来都挺简单的),但是人们常常在开始编码之前没有考虑这一问题。如果计划是“让我们一开始就编码”,那很好(有时,当我们对问题充分理解时,这是合适的)。至少,我们承认这是一个计划。

在这个阶段,我们可能还要决定一些另外的过程结构,但不是全部。可以理解,有些程序员喜欢用“休假方式”工作,也就是在开展他们的工作过程中,没有强制性的结构。“想做的时候就做”。这可能在短时间内是吸引人的,但是我发现,在进程中设立一些里程碑可以帮助集中我们的注意力,激发我们的热情,而不是只注意“完成项目”这个单一的目标。另外,里程碑将项目分成更细的阶段使得风险减小(此外里程碑还提供更多庆祝的机会)。

当我开始研究小说结构时(有一天我也要写小说),我最初是反对结构思想的,我觉得自己在写作时,直接下笔千言就行了。但是,稍后我认识到,在写涉及计算机的文字时,本身结构足够清晰,所以不需要多想。但是我仍然要组织文字结构,虽然在我头脑中是半意识的。即便我们认为自己的计划只是一上来就开始编码,在后续阶段仍然需要不断询问和回答一些问题。

1.9.1.1 任务陈述

无论建造什么系统,不管如何复杂,都有其基本的目的,有其要处理的业务,有其所满足的基本需要。通过依次审视用户界面、硬件或系统的特殊细节、算法编码和效率问题,我们将最终找出它的核心,通常简单而又直接。就像来自好莱坞电影的所谓高层概念(high concept),我们能用一句或两句话表述。这种纯粹的表述是起点。

高层概念相当重要,因为它设定了项目的基调,这是一种任务陈述。我们不必一开始就让它正确(我们也许正处于在项目变得完全清晰之前的最后阶段),但是要不停地努力直至它越来越正确。例如:在一个空中交通指挥系统中,我们可以从关于正在建立的系统的一个高层概念入手:“塔楼程序跟踪飞机”。但是当我们将这一系统收缩以适用于一个非常小的机场时,考虑将发生什么情况;可能只有一个控制人员甚至什么都没有。一个更有用的模型不应当像它描述问题那样多地关注正在创建的解决方案,例如“飞机到达、卸货、维修、重新装货和离开等”。

[1]一个极好的例子是Martin Fowler所写的专著《UML Distilled》(Addison-Wesley,2000),该书将复杂的UML过程简化为可管理的子集。

[2]我估计这样的项目有一条经验规则:如果不定因素不止一个,在没有创建一个能工作的原型之前,不要计划它将用多长时间和将花费多少。这里的自由度太大了。