UML软件工程组织

需求的实践 细节需求时期(下)

IBM DW中国站点
林星 (iamlinx@21cn.com)
2002 年 1 月

 

和业务建模时期不同的是,我不再花费笔墨讨论需求要如何做,因为做法、注意点和业务建模时期并没有什么太大的区别。而在完整的流程上,像RUP、XP之类的方法学可比我讲的要好的多。因此,我会把焦点集中在我在实际工作中的一些困惑,以及一些思考。

1、"和其它阶段的关系"的再思考。
上一篇的末尾我们简单的讨论了细节需求阶段和其它阶段的关系。对于软件过程的各个阶段,不同之处只是注重的焦点不同而已。在业务建模阶段,我们关注于系统的整体需求;在细节需求阶段,我们更关注于需求的细节部分。其它的阶段也是一样,架构阶段,所关注的当然是如何设计出一个合适的架构;到了设计阶段,注意力就转移到了如何设计方面。 当然,焦点的不同,导致了各个阶段所需要的技能和工具也不尽相同。业务建模阶段需要你有整体的把握能力,你可以使用简单的用例图,基本的用户素材等工具。细节需求阶段则要求你能够充分的挖掘需求和进行良好的沟通。相应的,在架构、分析、设计等阶段,也会有不同的需要。

各个阶段的实质就是注重点的不同,这对于大多数的软件开发而言都是一样的。不论你采用的过程是传统的,还是迭代的。

2、架构
Martin Fowler在他的ISA中写到:
I'm using architecture to mean the important design decisions, the ones that shape most aspects of a software system. These are often the harder decisions to change later on in the project (although often not as hard as people think). As a result these are ones where it's useful to start in the right direction, and thus knowing about architecture is a valuable skill for any software developer.

架构并不神秘,无非是一个决策而已,只是这项决策对软件特别重要就是了。软件开发人员可能根本没有主动的进行架构设计,但是他的行为,已经潜移默化的进行了;而还有一些人,总在谈论着架构,但是并不了解这个词的含义。 春运就要到了,学生也要赶着回家。如何选择回家的交通工具就是一个重要的决策。路程的远近、不同交通工具的价格都是显式的,比较容易知道的。而以往的经验也告诉你,火车票比较便宜,但是比较紧张,花费时间较长,整个过程也不舒服;飞机票比较贵,但是容易买到,花费时间少,整个过程很舒服。这时候你就要做出正确的决策,为回家选择一个正确的架构。你可能考虑的因素有哪一些?以下就列出了一些可能的因素:

拥挤程度,大家都知道春运时那种情况;
购买票的难易程度,火车票需要很早就要预定了;
是否有额外的渠道,比如你能够买到飞机的学生票;
经济实力,经过了一个学期的花销之后,现在还剩多少生活费;

根据这些因素,以及这些因素对你的影响程度,即优先级,你可以自己估算出一个或几个可能的架构方案。而这种架构的选择,直接影响到你之后的行为,也影响到行为的结果。

开发软件和买票回家又能有什么区别呢?一样要考虑成本、结果、时间。所不同的只是复杂了:需要专业技能,有很多不确定的因素。

那么我这里谈论架构的问题,和需求有什么关系呢?在上面的例子中,大家可以看到架构是根据什么决定的?是因素。因素的本质是什么?就是需求。因素,就是需求。所以,需求决定架构。上一篇中,我谈到说,在进行细节需求之前,一定要定出架构。当然,这时的架构可能只是一张草图。为什么?因为我们已经进行了业务建模,对项目有了一定的认识,对用户的需求也有一定的把握,成本、范围、时间等要素也都清楚了(如果你还不了解这些,请你回到业务建模阶段,你的工作还没有结束)。这个时候,决定架构的因素已经形成了,可以进行架构的设计了。 需求能够决定架构,架构反过来也能够影响需求。最明显的一个例子就是用户界面的设计。虽然用户界面主要来自于需求,但是不同的架构对用户界面也会有影响,例如基于浏览器的客户端,和基于Windows界面客户端能够提供的界面就不同。

在XP中,这种的架构设计,被称为Architecture Spike。为什么叫做Spike呢?就是说你需要专研,解决一些主要的问题,但你并不是提供一个完整的解决方案。例如,在项目的初期,如果你对新近的服务器和操作系统不了解,你肯定会花时间去了解,去试用,去测试;如果对数据库不了解,你也肯定会试着使用看看。这种行为,就是Spike。所以呢,架构的设计只是很初步的设计,而不是一个完善、定型了的设计。这一点要注意。

架构的选择和开发人员的经验和能力非常有关系。一般来说,它需要开发人员具有相关的经验。提供架构的厂商多如牛毛,如何选择,也是一项学问。比如,数据仓库的平台,选择的品种就很多;而Web Server的选择也是五花八门;操作系统也是一样。这些又都是和架构息息相关的。

3、模式
武功有招式,下棋有棋谱。不论是招式,还是棋谱,都是针对某一种问题的特定解决方案。在软件开发领域,这种解决方案就被称为模式。

"每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心,这样,你就能一次又一次的使用该方案而不必做重复的劳动"[AIS,77]

这句话源自模式的鼻祖――Christopher Alexander。而在软件开发领域,最值得一提的著作,就是GoF写的『设计模式』一书。在该书中,作者描述了23种的设计模式,可以说,这些模式奠定了面向对象设计的基础。而另一本值得阅读的著作,就是Martin Fowler所著的『分析模式』。

在软件设计中,我们遇到各种各样的问题,我们可能缺乏经验,或能力有限。这非常的正常。但是现实是,我们生产出的代码由于设计方面的缺陷,往往不够清晰,容易出错,难以扩展。相信只要是开发过软件的人,都遇到过这种问题。于是,我们就希望能够看看其他人,是如何处理这种问题的。这里的"其他人"指的可能是技术专家,也可能是经历过同种问题的人,他们对你遇到的问题有能力,有经验来解决。他们提出的解决方案往往是非常成熟的,能够解决你目前遇到的问题,也能够解决你目前尚未遇到的问题。

这对你的吸引力是很大的。那么,我们谈论模式,模式来源于何处呢。注意,模式是针对某一类的问题的解。这里的问题,所指的就是需求。比如说,客户希望能够在软件中实现不同的税率计算方法,那么,我们很自然的就可以想到Strategy模式;当客户的产品有一个很复杂的单位换算机制的之后,我们也能够很自然的想到Quantity模式。使用模式,能够使我们迅速的把握需求的解决方法。另一方面,模式往往告诉我们比目前的问题更多的方面,因为模式都是大家的经验沉淀。如上所说,它还可以解决你目前没有遇到的问题。因此,我们还可以从模式中发现出更深的需求。

另外一点也很重要,虽然它和需求的关系不大。模式往往都有一个模式名称。这就形成了一种沟通的语言。比如我们在下象棋的时候,只要说"马后炮",谁都知道这是什么意思,绝对不需要呢详细的解说每一步棋子的走法。对于软件开发也是一样的。我和我的同伴会心的一笑,说:"Factory Method。"两个人都能够理解这是什么意思,因为这是一种共通的语言,它代表了背后隐藏的各种类的关系和代码实现。这在软件开发中非常的重要。当然,这有个前提,大家都对模式有一个通透的了解。

4、简单设计
模式属于设计的一环,所以前面我们讨论的其实就是设计和需求的关系。这一小节我们还是讨论这个话题。但是侧重点有所不同。XP中有一个原则:KISS。不是kiss哦。它是Keep it simple and stupid的缩写。除了XP,其它的敏捷方法都提倡简单设计,反对过分设计(over build)。也就是说,针对目前的需求,设计目前的软件。

很多的软件人员都是完美主义者,他们喜欢完美的设计,然后把这种设计提供给用户。这种思想就是过分设计的开始。不必要的功能浪费了客户的投资。我们在做需求的时候,很经常发现开发人员随意的向客户推销一些新功能,或是在设计时,过多的考虑未来可能出现的需求变动。 所以,简单设计的意思,就是要针对目前客户提出的需求进行设计,不要过多的考虑目前没有提到的功能。

5、如何统一
细心的读者看到这里可能已经是疑窦丛生了。一边是鼓励使用、设计最完美的方案;另一方面,又强调简单。似乎是自我矛盾。不错这其中是由矛盾的地方,但也有一致的地方。因此,在使用先进的模式和保持设计的简单性之间,我们需要权衡。 一次,我们看到了一个关于权限设置的模式,我们觉得这个模式非常的好,它提供了类似于UNIX操作系统那样的权限控制,可以很随意的增加组、用户,并能够在文件级别上设置权限。我们如获至宝,觉得这是一个非常有用的模式。于是,我们花了一些时间将之实现,并打算应用于新的软件中。

在实际中,我们发现没有一个用户需要如此强大的功能,他们所需要的可能仅是密码控制,或是简单的用户管理。如此复杂的功能对他们来说没有任何的意义。完美的模式在现实中遭到了冷遇。

实际上,用户目前不需要这项功能,并不意味着他以后不需要这项功能。用户的计算机程度提高的很快,我们估计,再过那么一两年,用户可能就非常需要这项功能来管理他那日益庞大的员工队伍了。但是目前,这项功能对用户来说,设置复杂,所以他们并不愿意接受这个"有价值"的功能。

最后,我们想到了一个解决的办法,我们为这一项功能定义了一个通用的接口。通过这个接口,你可以实现复杂的功能,也可以实现简单的功能。总之,你只要是实现了接口的方法就行了。接口的方法很多,但是在目前的简单的实现中,可能就仅仅是一个返回语句,并没有很多的实现。相应的,用户的也仅仅需要使用简单的方法,诸如addUser(),updatePasswd()等。界面也做了简化的处理。

通过这种方法,我们可以在用户需要的时候,替换掉权限模块,对其它的应用并不影响。当然,其中有很多的细节需要注意。在完成之后,我们还发现了另一个副产品:整个的权限控制模块是高内聚,低耦合的。

模式,就好比螺帽的设计图纸,它规定了螺帽的技术规格,但是没有规定具体的尺寸。对应于不同的需要,我们可以设计出不同大小的螺帽以供使用。图纸只需要一份就可以了,并不需要为每一种尺寸的螺帽画一张图纸。

因此呢,一个经历了实践检验的模式,往往功能强大。但是它可以有多种不同的实现。有哪一条法律规定你必须完全的实现一种模式呢。在实际使用中,你需要权衡模式使用的成本和效益。你会发现,在很多情况下,你还是实现了模式,但只是模式的一个框架,或一部分,但是这在目前就够用了。而当需求变动,或新的需求出现的时候,你可以很方便的在原有搭起的框架上添砖加瓦。一切都是那么的自然。比如,在一个进销存的软件中,存在一个运输选择算法,目前的运输途径就一种,但是很有可能会增加新的途径。这时候,你就决定采用Strategy模式。但是你只是实现了一个简单的框架,只有一个具体子类,来实现目前的算法。当新的运输途径出现时,你需要增加一个算法,你就可以再增加一个子类,只要实现的接口相同,改动是不会影响到系统其它的部分的。

6、测试
测试是非常重要的,是软件成败的关键。但是目前国内对测试并不关注,或是假装关注。这样就无法保证软件的质量。测试有很多种,我们这里要说的是和需求息息相关的测试,就是接收测试(Acceptance Test)(关于这个词,可能不同的文章会有不同的译法)。
Surely you aren't going to assume you're getting what you need. Prove that it works! Acceptance tests allow the customer to know when the system works, and tell the programmers what needs to be done.

上面这段话摘自『XPInstalled』。接收测试有两个作用:首先是让客户明白系统能够做什么,然后是让程序员明白该让系统做些什么。

有一种测试的方法是把测试留到最后才做,让客户去发现错误。千万别这么做。原先花费1块钱就可以改正的错误,到了这个时候,可能需要花费1000块钱才能解决。这是软件开发的放大效应。因为很多的工作已经基于这个错误建立了,开发人员也已经对这个错误没有映像了。

XP中有一个很重要的价值,叫做反馈(Feedback)。Kent Beck在Extreme Programming Explained中有句话讲得非常好:"乐观是编程的职业病,反馈则是其处方。"从需求的识别,到根据需求构建的系统交付给客户,客户提出意见。这就是一个反馈过程。反馈过程所花费的时间越少越好。接受测试就是解决客户反馈的一个有效的手段。客户编写可重复的测试脚本,开发人员开发出的软件要经受这个测试脚本的考验。这种的反馈速度是很高的,能够有效的解决反馈的问题。

因此,客户在要求实现需求时,同时也有义务提供相关的接收测试:
if it's worth having the programmers build it, it's worth knowing that they got it right.

一个有价值的功能一定是可测试的,如果不是,那么这个功能要么是没有价值,要么是尚未描述清楚。 要注意,这个测试必须是可重复的。这是因为需求的易变性。需求在不断变化,这就意味着代码在不断的变化。因此,原先已经用接收测试证明过了的代码还需要再次证明。这就要求测试是可以重复的。

大量的测试,甚至重复的测试引出了一个新的问题:全凭手工进行测试会浪费大量的时间。因此,易变的需求对测试提出了一个新的要求:自动化测试。只有自动化的进行测试,才可以完成如此大量的测试工作。目前,最好的自动化测试工具,应该就是Xunit系列。关于这方面的问题,大家可以参考相关的资料,这里我们不作深入的讨论。

接收测试最可能的一种形式就是自然语言的说明,外带一组的测试数据。需要强调的一点是,这个测试数据一定要包括输入和输出。这样才可以做出比较。如果只有输入没有输出。测试很可能就是白费劲。所以,计算这个输出是手工计算的,它也是必要的。

除了接收测试,还有一个很重要的概念就是单元测试(Unit Test)。但是单元测试和设计的关系比较大,和需求没有过多的关系,我们这里就只是点一下。大家如有兴趣,可以参考相关的资料。

7、分解
对于需求来说,需要用到的最多的技能可能就是分解的技能了。可以说软件科学最重要的思想就是"分而治之"的思想。不是吗?面向对象是不是一种分解的思想。类、组件、包,这种层级结构不正是体现了"分而治之"吗?当然我这里可能把包和组件弄混淆了。但我的主要目的是要指出这种分解的方法在软件开发中是无处不在的。对于需求也是一样。我们最早做的业务建模就是为了得到一个系统的概貌图。然后到了细节需求阶段,我们会把这张图放大,细分,一直到我们有把握处理为止。

如何把100块砖累起来呢?如果一块一块的累,那很容易就倒了。一种比较好的做法,就是先10块10块的打包,这样,100块砖就变成了10个砖包,再把它们累起来就容易的多了。面向对象其实就是这么做的,当然,面向对象的实现比累砖要难多了。 我们看看XP中是如何处理分解的。XP中最大的周期叫做发布版(release),一个发布版需要一个月或几个月的时间,一般不超过6个月。发布版的末期,需要向客户提供一个可以运行的软件的发布版。然后,XP还定义了次级的周期,称为迭代(iteration),一次的迭代大概需要一周或几周的时间。每一次的迭代都是一个"伪发布版"(pretend release),就是说,迭代的最后也必需要提供一个可以工作的软件,这个软件随时都可以发布。为了定义一次迭代中要处理的事情,XP还定义了素材(story),一份素材代表了客户希望在软件中出现的一项功能(feature),一份素材是很小的,它只需要花费一个开发人员几天或几周的时间。素材已经很小了,但是还需要细分到任务(task),一项任务只需要1到4天的工作量。这并不是最小的单位,XP还把任务划分到测试(test),但这已经到个人级别上了。

我们看到,XP从客户到每一个开发人员,定义了5级的单位,并把时间精确到了天、小时。所以我有时候听人说,XP好像对开发人员的要求不严格。错了,XP可以说是目前要求最为严格的方法,这种分类方法可见一斑。这种分类有什么好处呢?

首先,我们来看发布版,发布版的最短时间是一个月,也就是说,最频繁是一个月向客户交付一次软件。这样,就在很大程度上保证了反馈的迅速进行。避免了传统的开发方法中最后客户发现问题时已经太迟了的现象。

再看迭代,迭代之所以被称为伪发布版,就是因为它的产出物的性质和发布版是一样的。都是可以交付的产品,但是迭代的功能比较少,没有必要交付给客户。我们知道,在传统的软件开发中,整合是一个大问题。整合必然会出现问题,但是没人知道问题在哪儿。所以呢,XP通过不间断的整合,避免了这个问题。

但是,迭代的周期如此之短,能够做些什么呢。这就是素材出现的原因。素材由客户提出,由客户决定优先级,由客户决定放在哪一次的发布版和迭代中。同时,如上面我们谈到的,客户需要为素材制定接收测试。

一份素材的周期是几天或几周,而一次迭代的时间也就几周。这说明素材对迭代而言,单位过大了。所以要把素材再次分解为任务。每一个任务都需要一个开发人员认领,签字。在分解素材的过程中,我们可以发现有一些任务是多个素材共享的,这个概念和用例中的使用的概念是一致的。开发人员会估计自己的任务所需要的时间。从而得出一个迭代周期的总时间。这个时间的估计是基于以往的经验的,因此随着项目的进行,这个估计往往会越发的准确。

我们这里讲XP的分解哲学,并不是要求大家也依样画葫芦,你真要那样做也不行。因为XP的各项实践都是一体的,只有实现了所有的实践,才能体现其威力。我们这样断章取义是行不通的。但是其中的思想我们是可以借用的。分解、归类、排序、估计。这种分析问题的方法,也是我们值得借鉴的。这里有几点要注意:

最早的分解依据往往是客户要求的时间。例如,客户希望在3个月内看到什么什么。在客户和开发放商议之后,决定制定两次的发布版。这个决定虽然开发方可以提供参考意见,但是决定权在客户方;
素材的提出,素材的优先级,素材的安排,这些都是客户的权利。客户也有权利取消、增加、修改素材,或是改变其优先级;
客户投入多少,就能够看到多少的软件。客户也可以取消目前的项目,而且,先前的投资也会有相应部分的软件产出;
对于开发时间的估计是开发人员的权力,其它任何强加于开发人员的时间安排都是不对的;
任务的选择也是出于开发人员自愿的原则;
如果开发人员估计出的总的时间和迭代的预计的时间有出入,绝对不能牺牲开发人员的额外时间换取迭代在限定时间内完成。这种做法无异于饮鸩止渴。比较好的做法是去掉一些任务。

8、客户如何参与
这是一个纯粹的补充问题。一次,一位读者写信来问我,中国的客户素质比较低,对计算机不了解,XP强调的现场客户现实中根本做不到。

XP要求开发人员在开发软件的过程中随时予以支持。诚然,要做到这一点是很难的。我自己也遇到过这种难题,知道问题的棘手程度。我想,这个问题可以分为两个方面。

一种是定制的软件。对于这种项目,你需要解决用户的参与问题,往往客户方的老总愿意投入资金,但是却不愿意投入支持的人力。我也曾见过客户方专门招了一个应届生来对付需求的情况。遇到这种情况,如果你没有能力克服它,那么这个项目绝对是失败的。即便项目最后成功了,申请了什么"重大攻关"之类的奖项,你我都很清楚,这只不过是表面功夫而已。造成这种问题的原因有很多,你需要分析它们,列举出最深层的原因,并且保证列出的这些原因之间并不会相互影响。然后再试着来解决它。一般而言,如果客户方是真正愿意实现这个软件的话,事情并不是不可解决的。

另一种是产品化的软件。我们可以把这种软件的客户分为三类:市场人员、领域专家、最终用户。市场人员往往对软件有最初的认识,但是这种认识往往不够深入。所以,在开发产品化的软件时,一定要配备专门的市场人员,他们对客户的需求最了解,是需求的第一手来源。既然你的老板想要开发这个软件,说明对软件的未来市场有期待,市场人员加入的要求应该是可以得到满足的。其次是领域专家,现在很多的软件公司都配备有领域专家,他们的作用可不小,和市场人员相比,他们也同样熟悉需求,因为他们自己就是资深的用户,并且他们还熟悉理论,能够为软件开发出大力气。最后是最终用户,前面的两类人的参与还不够,如果最终用户不认可软件,那还是没有用。所以,要求市场人员找寻可能的目标客户,试用软件,提出意见,是非常重要的。我们常说的Beta测试也就是这样做的。

9、结语
最早这片文章的想法是来自于一个信贷系统(以文章的观点来看,这个系统是一个失败的案例)。除此之外,还加上了一些以前的项目的经验,以及朋友的一些经验。最后综合成了这一篇文章。 这个系列的文章的写作时间的跨度很长。在这个期间,我的思想也经历一个很大的转变,所以在写作中也出现过对已经完稿的部分大肆删修的情况。这篇文章虽名为『需求的实践』,但所提到的各种想法、方法,都有理论的依据。当然也是参考了很多的资料。而我个人的能力又极为有限,所以有些时候是心有余而力不足。在这样的情形下,文章难免会出现很多的错误。还希望能得到读者们的谅解和指正。

在写作的过程中,也有很多的热心人写来信件,其中好些还成为了好朋友。曾经想把里面的问题整理出来,但是由于疏忽,其中的一部分已经找不到了。希望还能有机会做这件事情。 参考资料:

Karl Wieger:《Software Requirements》
Scott W. Ambler: AgileModeling: http://www.agilemodeling.com
Scott W. Ambler: The Object Primer 2nd Edition
Jacobson, I., Booch, G., and Rumbaugh, J. (1999). The Unified Software Development Process
GOF:《Design Patterns: Elements of Reusable Object Oriented Software》
Eric Gamma, Kent Beck: Junit: http://www.junit.org
Kent Beck,Martin Fowler:《Planning Extreme Programming》



版权所有:UML软件工程组织