OOA&D实践之路——真实案例解析OO理论与实践(二)
 

2010-01-08 作者:张洋 来源:张洋的blog

 

五、需求分析之前的故事

高质量软件的第一要素

到目前为止,我们做了很多工作,但是我一直在强调这些都还不是需求分析。在很多人心目中,软件开发的第一件事就是先做需求分析。那么我们为什么不这样做呢?这牵扯到一个关键的问题:我们都希望开发高质量的软件,而本系列文章的重点也是如何通过OO实践开发高质量软件,那么什么是高质量软件?

对于这个问题,也许很多人会说,是灵活的、是易于修改和扩展的、是可维护性高的、是用户体验好的、是文档完整的、是代码规范的、是性能处理优秀的……好吧,我承认,这些都是高质量软件必不可少的元素,但是,还有一个更重要的要素,就是:软件必须做客户希望它做的事。你的软件再灵活、编码再规范,客户不关心,客户最关心的是软件是不是完成了他期待的功能,可以做他希望软件做的事。所以,高质量软件的第一要素就是:让软件做客户希望它做的事。

知道了这点,就知道为什么第一步不是做需求分析了,因为需求分析的重点不是“让软件做客户希望它做的事”,而是“将需求分解归纳成开发人员容易进行领域分析和设计的信息片段”。所以,需求分析是开发人员面的东西,而不是客户面的东西。作为开发人员,我们要首先站在客户的角度看问题,而不能总是站在开发人员角度,和客户隔着一条河对话。我们要走过去,去河的另一岸。

回顾我们的工作

现在来总结一下我们目前所做的工作,你会发现,我们所做的全部工作,其目的就是让软件做客户希望它做的事。

我们首先总结出特性列表,然后通过分析和询问降低了风险,同时修改了特性列表,最后从做出一张用例图,使得从全局角度对系统进行一个概览。所有这一切,其实都是开发人员在“努力变成客户”,或说努力让自己站在客户的角度看系统,真正了解客户想让希望做什么。因为,最好的理解需求的方式就是理解客户想让系统做什么。

我们在哪里?看看地图吧

做了这么多工作,是不是有点迷失方向的感觉?似乎我们已经迷失在OO从林中,不知现在身在何处。好的,那我们看看“OO地图”吧,一方面搞清楚我们在什么地方,另一方面看看我们后续有哪些路要走。

以上就是实践中的大致开发流程。一般来说,开发大致分为两个阶段:前一阶段我们要站在用户角度,搞清用户想要系统做什么;后一阶段要回到开发人员角度,进行分析、设计、编码、测试等一系列操作。而我们现在正处在两个阶段的交界处。

一般在迭代阶段提倡使用迭代与增量的方式进行开发。至于这样有什么好处,以及OO如何于迭代增量方式结合这些问题,我们将在下一篇文章中结合我们的案例详细讨论。

重点总结

      1.高质量软件的第一要素是:软件做客户希望它做的事。
      2.在开发初期,我们要尽量站在客户角度。
      3.理解需求的最好方法是明白客户希望软件做什么。
      4.开发流程大约分为两个阶段:搞清用户想要系统做什么和迭代开发。

六、迭代式开发与用例驱动

再次明晰开发流程

在上一篇文章“五、需求分析之前的故事”中,我给出了一幅开发流程图:

这幅图,加上前几篇文章的内容,给不少朋友留下诸多困惑。如“特性列表不算需求分析吗?”、“用例图怎么跑到需求分析前面去了?没有需求分析哪来的用例图?”为了解开这些困惑,我们应该先把开发流程各个相关概念给明确了。

在一般开发流程中,直观上可以分为两个部分:业务阶段和系统阶段。明确这一点非常重要。其中业务阶段主要进行的工作是与计算机无关的,而系统阶段才是和计算机相关的东西。上图中“我们在这里”那道竖线所指的地方,就是业务阶段和系统阶段的分界点。

而如果从建模角度分析,整个开发流程分为四个子流程:

      a)业务建模流程
      b)系统建模流程
      c)分析设计建模流程
      d)部署实施建模流程
      其中a)属于业务阶段,而b)c)d)均属于系统阶段。

业务建模流程的任务就是做业务分析、降低风险和给出系统总体概览模型。系统建模主要就是需求工程。分析设计建模就是我们熟悉的概要设计和详细设计。而部署实施建模是对系统的具体实施建立模型。

其中,我们一般所说的“需求”,实际是指“系统需求”,是在b)阶段进行的,所以a)阶段的一切工作,都不能算是需求分析。(当然,如果加一个前缀,说是“业务需求分析”,也是可以的,但是一般在软件工程领域,说“需求分析”是特指“系统需求分析”。)

而关于用例图的疑惑,大家应该知道,用例图分为业务用例图和系统用例图,而业务用例图产生于a)流程,这样它们当然会出现在b)流程,也就是需求分析之前。当然,在b)流程中还会出现用例图,这时就是系统用例图了。

明白了以上开发流程,我想很多疑惑就自然解开了。所以,其实我们所谓的“需求分析之前的故事”,就是业务建模流程。

系统阶段的法宝1:迭代与增量

在上文中,我们提到软件过程大体分为业务阶段是系统阶段。在前面几篇文章中,我们已经基本完成业务阶段,下面是进入系统阶段了。不过莫急,在正式进入系统阶段之前,我们有几个重要的、关于系统阶段的话题要聊一下。

如果你问我,在系统阶段最应该被牢记的是什么,我会告诉你,是两个词:迭代&增量。

我们已经知道,系统阶段是在业务阶段的基础上,在计算机领域内进行需求分析、系统分析、概要设计、详细设计、编码、测试、部署实施等一系列环节。但是,如果你想仅施行一遍这个流程,而完成整个系统的话,不好意思,你大致会精神崩溃。

如果这个过程仅实施一遍,其实就成了瀑布模型。小系统也许可以,但是稍大点的系统,将会带来严重的后果。我们知道,“软件开发中永远不变的就是变化”,如果采用瀑布模型,反馈将会非常靠后,一直要到部署实施阶段才能看到成果,如果客户要求更改需求或认为当前系统不是他们想要的,那么修改过程会异常惨烈,代价也是巨大的。

那么,我们应该如何实施系统阶段开发呢?法宝就是迭代和增量。具体做法是:

我们将整个系统分为许多相对独立的“单元”,每次只对一个或少数几个单元实施需求分析、系统分析、概要设计、详细设计、编码、测试、部署实施等一系列过程,一次这样的过程叫做一次迭代,而将一次迭代的成果加入现有成果的过程就叫做增量。

这种开发流程的优势是明显的:我们总是能在相对较短的时间内,完成整个系统功能的一个“子集”,这个子集是可以运行的,可以看到效果,所以如果用户不满意,反馈是及时的,修改代价也较小。通过合理的过程控制,变更代价总可以控制在一个可以接受的范围内。

在实施迭代&增量过程时,要注意一下两点:

1)迭代单元不是环节,而是系统功能的某个子集。如不能说第一次迭代完成需求分析、第二次迭代完成设计……这不叫迭代式开发,这叫里程碑式开发,和迭代有本质区别。

2)每次迭代一定要完整完成需求分析、系统分析、概要设计、详细设计、编码、测试、部署实施这一套环节,产生的成果必须是成品,是可运行、可交付的,是整个系统的一个子集,而不能是一个半成品。

系统阶段的法宝2:用例驱动

相信,大家或多或少听说过“用例驱动”。那么什么是用例驱动呢?如果你理解了上文的迭代&增量过程,就很好理解用例驱动了。上文不是说过,迭代时每次选取一个或少数几个“单元”吗?那么这个“单元”是什么?理论上,什么都可以,只要是对系统合理的划分。但在实践中,人们发现,使用业务用例作为迭代单元是最合适的。为什么呢?
      其一,业务用例大小适中,规模平均。
      其二,业务用例大多相互独立性好,适合进行迭代。
      其三,业务用例能很好反映系统的功能单元。

所以,所谓的用例驱动就是以业务用例作为迭代单元进行迭代开发的流程。

你看,我们前面做了那么多业务分析,其中成果之一就是业务用例图。而这里,业务用例成了我们进行系统迭代开发的单元和驱动者,所以,软件开发过程不是割裂的,而是相互联系的,不会说上一个阶段过去就过去了,和下一阶段毫无联系。一般,上一阶段的产品总会成为下一阶段的材料。

在这里附上一副我自己画的示意图,希望能帮助大家理解本文内容:

本文理论讲得多了点,有点枯燥,但是必须讲,因为明白这些是以后进行系统阶段的基础。从下一篇开始,我们回归示例,进入真正的系统阶段了。下一篇,将开始第一轮迭代的起始阶段:需求分析。

重点总结

      1.软件过程大致可分为业务阶段和系统阶段。
      2.业务阶段进行业务建模流程,与计算机领域无关;系统阶段进行系统建模、分析设计建模和部署建模阶段,与计算机相关。
      3.系统阶段的两大法宝:迭代式开发与用例驱动。

七、【第一轮迭代】需求分析与领域分析

在前面,我们花了六篇文章的篇幅去讨论需求分析之前发生的事情,这些内容看起来枯燥或飘渺,但实际是为真正开始系统的分析、设计和实现进行的必要准备。从这篇开始,将正式进入系统的开发阶段。这一篇文章,将讨论第一轮迭代过程中的需求分析和领域分析环节。

选取第一轮迭代要实现的特性

回顾前面章节,我们说到,“迭代与增量”和“用例驱动”是系统开发的两大法宝。另外,指出了如下几个要点:

1)迭代单元不是环节,而是系统功能的某个子集。如不能说第一次迭代完成需求分析、第二次迭代完成设计……这不叫迭代式开发,这叫里程碑式开发,和迭代有本质区别。

2)每次迭代一定要完整完成需求分析、系统分析、概要设计、详细设计、编码、测试、部署实施这一套环节,产生的成果必须是成品,是可运行、可交付的,是整个系统的一个子集,而不能是一个半成品。

所以,说白了,这第一步就是要从前面得到的特性列表或业务用例分析文档中选取一个或几个特性或业务用例进行实现。(因为特性和业务用例有对等映射关系,所以,从特性列表或业务用例分析文档中选取迭代功能点在理论上是等价的。)这里,我们从特性列表选取特性。那么首先把我们前面完成的特性列表再搬出来:

上图就是我们前面得出的特性列表,总共有六个特性。理论上,第一轮选取哪些,总共选取几个,没有明确的规定,但是,在选取特性方面,还是有一定经验可以遵循的,大致有如下原则:

1)就选取个数来说,与迭代周期有关。一般的迭代开发中,一个迭代周期大多选在一周到两周之间,不宜多于两周,所以,每个周期选择的特性要估算能在这个周期内完成。(顺便说一下,在每个周期内,如果发现时间不够,做不完计划,应削减一些特性推入下个周期,切不可延长周期。另外,切不可在周期开始后添加任务。)

2)就选取的特性来说,最好是比较内聚的几个特性。也就是说,每个周期选取的特性,要尽量关联紧密,而和其他周期的特性要尽量耦合度低。

3)就选取顺序来说,因为要求每一个周期都要产生一个可运行的子集系统,所以,最好先选取平台性、基础性、对外依赖弱的特性,再选取功能性、对外依赖强的特性。例如,在这个例子中,如果第一轮迭代选择特性2则不是一个好主意。因为购物车功能要依赖于管理员、加盟商、物料等诸多特性,在后者没有实现之前,很难单独做出一个可运行的购物车子集系统。

针对以上原则进行考虑,笔者最终选择3、4和6作为第一轮迭代的特性。

至于为什么这样选,我就不再分析,请各位结合我上面提到的三个原则自己思考一下。

领域分析

在确定迭代特性后,下一步要进行领域分析。领域分析就是针对特性涉及到的实体及实体间的关系进行一个分析,构造出静态领域模型。这里要注意三点:1、领域模型是静态模型;2、领域模型要反映的是实体及实体见得静态关系(例如包含等,而调用这类动态关系不该在这里出现);3、领域模型是比较高视角的模型,其中的实体和最终程序中的实体类未必对应。

领域分析的方法有很多,下面我使用一种我总结的领域分析方法。流程如下:

step1、提取特性中的名词

我们这个迭代周期内涉及的特性是3、4和6,从中可提取出如下名词:加盟商,连锁店,网络,管理员,系统,直属连锁店,原价,等级,折扣。注意,这些名词就是领域实体的候选者。

step2、筛选名词,确定实体

不是所有候选名词都是领域中的实体,这一步要对候选者进行筛选。这一步非常重要,也相对难度较高,需要结合经验和前面的客户谈论材料、需求记录等,必要时,要咨询客户或业务专家。下面我们进行筛选。

首先,“网络”显然不是领域内的实体,排除。“系统”是对整个产品的总称,也比较明显不是领域实体,排除。排除了两个明显的“干扰项”,下面看几个可疑的家伙,这里,我发现“原价”、“等级”和“折扣”比较可疑。通过对前面材料的回顾分析,基本可以肯定“原价”应为物料的一个属性,难以成为单独的实体,排除。而“折扣”和“等级”是否能成为实体,依赖于后续系统的设计方式,如果将等级单独设计成一个模块,则其应该为一个实体,而若将其设计为加盟商的属性,则不能成为实体。对于这种摸棱两可的候选项,我们姑且保留,并加一个“?”作为后缀。当然,由于前面分析中说明“折扣关联与等级”,所以,仅保留“等级”就可以了,“折扣”可作为其属性。

经过上述筛选,获得下列领域实体:加盟商,连锁店,管理员,直属连锁店,等级?

我们不妨先将各个实体画入领域分析图。

step3、确定实体间的关系

有了实体,下面要确定各个实体间的关系。如上图所示,管理员比较容易确定,因为它似乎不与任何实体存在静态关系。这里比较纠结的是加盟商、连锁店、直属连锁店和等级四个实体。关于它们间的关系我们不能臆想,要去查阅前面的记录资料或去咨询客户。在OOA&D实践之路——真实案例解析OO理论与实践(三、降低风险)一文中,我们记录了这样一段文字:

“加盟商和连锁店不是一个概念,加盟商直属总公司,连锁店可能直属总公司也可能直属某加盟商。加盟商和直属总公司的连锁店直接向总公司定料,不直属的的连锁店向相应加盟商领取原料。连锁店按原价定料,加盟商按照等级分为5级,每级折扣不同。”

从以上文字中,我们提炼出如下关系:

1)加盟商和连锁店没有关系。

2)直属连锁店是连锁店的一种特例。而且应该还有种“非直属连锁店”

3)非直属连锁店可能会属于某个加盟商。

4)等级仅属于加盟商。

到这里为止,几个实体间的关系基本清晰了,我们将其表达在领域分析图里:

以上就是第一轮迭代的领域分析图了。其中直属连锁店和非直属连锁店都继承于连锁店,而非直属连锁店必定对应一个加盟商,加盟商可以对应零到多个非直属连锁店,每个加盟商有且只有一个对应的等级。

step4、领域分析的优化

别觉得完成上述步骤就万事大吉了,虽然我们的领域分析模型已经出来了,但它可靠吗?还有优化的余地吗?万事多思考一点,总是没坏处的。就像刚出厂没经过检测的飞机,你敢坐吗?所以,这里我们要对已经成型的领域分析模型进行一个检测。检测的方法很简单,就是回顾前述工作,逐个点进行验证和确认。当我回顾了特性及初步业务需求后,发现如下两点问题:

第一、管理员是否是领域中的实体?要知道,领域中的实体,一定是系统边界内的实体。那这里就要分情况讨论了,如果系统只需要一个管理员,那么系统中就没有对管理员的管理,那么管理员只是系统外部的用户,就不能存在于领域实体中了;如果有多个管理员,并且管理员管理单独成为一个模块,则管理员应放在这里。于是我联系了客户,在确认系统只需要一个管理员后,我毫不犹豫将管理员从领域模型中抹去了。

第二、我觉得领域模型抽象层次还有所欠缺。加盟商和连锁店性质很类似,都有注册、都要被审核,其实它们应该继承自某个更高抽象,这里我称其为会员。于是经过思考和修正,得到最终的领域模型如下:

这里要注意,领域模型只表示出实体和实体间的静态关系就好了。至于各个实体有哪些属性,有哪些交互,那是后面设计阶段的工作。

基于用例的需求分析——编写用例图及用例规约

传统的软件工程中,通常使用需求规格说明书进行系统需求分析。而我更喜欢使用用例分析技术。在用例分析阶段,我们要输出两份文档:用例图和用例规约。用例图用于概观上描述需求,用例规约用于对用例的流程进行详细描述,直接作为后续设计、编码及测试的依据。

下面进行系统需求分析。

step1、提取特性中的动词

上面我们曾使用提取名词法找实体,那么要找用例,就要分析特性中的动词了。废话不多说,我们先提取出特性3、4和6中的动词:注册,审核,使用,管理,定料。因为用例分析中要确定每个用例的Actor,所以,在提取完动词后,要对每个动词赋予其主语,作为其Actor,于是,最终得到的提取列表如下:注册【未注册加盟商和连锁店】,审核【管理员】,使用【注册后的加盟商和连锁店】,管理【管理员】,定料【注册后的加盟商和连锁店】

step2、动词筛选

同样的,不是每个动词都是合法的用例,这里也要对候选动词进行筛选。具体到这里,“使用”是个很泛的名词,不应该成为用例,而“定料”虽是用例,但于这轮迭代的内聚性不高,建议放入后续迭代中。最后得到用例:注册【未注册加盟商和连锁店】,审核【管理员】,管理【管理员】

step3、用例分析与优化

这里的用例有进一步分析的余地,首先,“管理”这个用例粒度太大,很难知道设计和开发,于是应进行分解;其次,“审核”应属于管理的一部分。基于以上两点,将管理分解为“删除会员”、“指定加盟商等级”和“审核会员”,另外,“未注册的加盟商和连锁店”,跟据领域分析,可写作“未注册会员”。另外,这里有几个隐含的用例,就是关于等级的管理,这几个用例从动词分析中是提取不出来了,这取决于特性描述的局限性。我们通过经验和个人的分析,得到关于等级的管理用例。于是,最终用例敲定如下(这里用例命名规则为 uc迭代周期编号.用例编号):

uc1.1 - 注册 actor:未注册会员

uc1.2 - 删除会员 actor:管理员

uc1.3 - 指定加盟商等级 actor:管理员

 uc1.4 - 审核会员 actor:管理员

uc1.5 - 添加等级 actor:管理员

uc1.6 - 删除等级 actor:管理员

uc1.7 - 等级信息维护 actor:管理员

step4、给出用例图

跟据上述分析,给出用例图如下:

step4、编写用例规约

需求分析的最后一步,就是为每个用例编写相应的用例规约。用例规约是一种规范化文档,它面向设计开发人员,详细描述了各个用例内部的基本信息、执行流程及例外流程等,必要时可附上相应的活动图。因为用例规约是设计和开发的重要参考文档,所以在编写过程中务必要做到详尽和准确。

下面仅给出“注册”这个用例的用例规约作为示例和参考。

必要时,可在下端附上活动图:

总结

在每个迭代周期的初始阶段,当选择完需要迭代的特性后,就可以开始领域分析和需求分析了。本文是先进行领域分析。其实两者并无确定的先后顺序,往往是相辅相成,交叉进行。下一篇文章将进入第一迭代周期的设计阶段。

作者:T2噬菌体 出处:http://leoo2sk.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。


火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织