作者:张洋
在前面,我们花了六篇文章的篇幅去讨论需求分析之前发生的事情,这些内容看起来枯燥或飘渺,但实际是为真正开始系统的分析、设计和实现进行的必要准备。从这篇开始,将正式进入系统的开发阶段。这一篇文章,将讨论第一轮迭代过程中的需求分析和领域分析环节。
选取第一轮迭代要实现的特性
回顾前面章节,我们说到,“迭代与增量”和“用例驱动”是系统开发的两大法宝。另外,指出了如下几个要点:
1)迭代单元不是环节,而是系统功能的某个子集。如不能说第一次迭代完成需求分析、第二次迭代完成设计……这不叫迭代式开发,这叫里程碑式开发,和迭代有本质区别。
2)每次迭代一定要完整完成需求分析、系统分析、概要设计、详细设计、编码、测试、部署实施这一套环节,产生的成果必须是成品,是可运行、可交付的,是整个系统的一个子集,而不能是一个半成品。
所以,说白了,这第一步就是要从前面得到的特性列表或业务用例分析文档中选取一个或几个特性或业务用例进行实现。(因为特性和业务用例有对等映射关系,所以,从特性列表或业务用例分析文档中选取迭代功能点在理论上是等价的。)这里,我们从特性列表选取特性。那么首先把我们前面完成的特性列表再搬出来:
上图就是我们前面得出的特性列表,总共有六个特性。理论上,第一轮选取哪些,总共选取几个,没有明确的规定,但是,在选取特性方面,还是有一定经验可以遵循的,大致有如下原则:
1)就选取个数来说,与迭代周期有关。一般的迭代开发中,一个迭代周期大多选在一周到两周之间,不宜多于两周,所以,每个周期选择的特性要估算能在这个周期内完成。(顺便说一下,在每个周期内,如果发现时间不够,做不完计划,应削减一些特性推入下个周期,切不可延长周期。另外,切不可在周期开始后添加任务。)
2)就选取的特性来说,最好是比较内聚的几个特性。也就是说,每个周期选取的特性,要尽量关联紧密,而和其他周期的特性要尽量耦合度低。
3)就选取顺序来说,因为要求每一个周期都要产生一个可运行的子集系统,所以,最好先选取平台性、基础性、对外依赖弱的特性,再选取功能性、对外依赖强的特性。例如,在这个例子中,如果第一轮迭代选择特性2则不是一个好主意。因为购物车功能要依赖于管理员、加盟商、物料等诸多特性,在后者没有实现之前,很难单独做出一个可运行的购物车子集系统。
针对以上原则进行考虑,笔者最终选择3、4和6作为第一轮迭代的特性。
至于为什么这样选,我就不再分析,请各位结合我上面提到的三个原则自己思考一下。
领域分析
在确定迭代特性后,下一步要进行领域分析。领域分析就是针对特性涉及到的实体及实体间的关系进行一个分析,构造出静态领域模型。这里要注意三点:1、领域模型是静态模型;2、领域模型要反映的是实体及实体见得静态关系(例如包含等,而调用这类动态关系不该在这里出现);3、领域模型是比较高视角的模型,其中的实体和最终程序中的实体类未必对应。
领域分析的方法有很多,下面我使用一种我总结的领域分析方法。流程如下:
step1、提取特性中的名词
我们这个迭代周期内涉及的特性是3、4和6,从中可提取出如下名词:加盟商,连锁店,网络,管理员,系统,直属连锁店,原价,等级,折扣。注意,这些名词就是领域实体的候选者。
step2、筛选名词,确定实体
不是所有候选名词都是领域中的实体,这一步要对候选者进行筛选。这一步非常重要,也相对难度较高,需要结合经验和前面的客户谈论材料、需求记录等,必要时,要咨询客户或业务专家。下面我们进行筛选。
首先,“网络”显然不是领域内的实体,排除。“系统”是对整个产品的总称,也比较明显不是领域实体,排除。排除了两个明显的“干扰项”,下面看几个可疑的家伙,这里,我发现“原价”、“等级”和“折扣”比较可疑。通过对前面材料的回顾分析,基本可以肯定“原价”应为物料的一个属性,难以成为单独的实体,排除。而“折扣”和“等级”是否能成为实体,依赖于后续系统的设计方式,如果将等级单独设计成一个模块,则其应该为一个实体,而若将其设计为加盟商的属性,则不能成为实体。对于这种摸棱两可的候选项,我们姑且保留,并加一个“?”作为后缀。当然,由于前面分析中说明“折扣关联与等级”,所以,仅保留“等级”就可以了,“折扣”可作为其属性。
经过上述筛选,获得下列领域实体:加盟商,连锁店,管理员,直属连锁店,等级?。
我们不妨先将各个实体画入领域分析图。
step3、确定实体间的关系
有了实体,下面要确定各个实体间的关系。如上图所示,管理员比较容易确定,因为它似乎不与任何实体存在静态关系。这里比较纠结的是加盟商、连锁店、直属连锁店和等级四个实体。关于它们间的关系我们不能臆想,要去查阅前面的记录资料或去咨询客户——
“加盟商和连锁店不是一个概念,加盟商直属总公司,连锁店可能直属总公司也可能直属某加盟商。加盟商和直属总公司的连锁店直接向总公司定料,不直属的的连锁店向相应加盟商领取原料。连锁店按原价定料,加盟商按照等级分为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、编写用例规约
需求分析的最后一步,就是为每个用例编写相应的用例规约。用例规约是一种规范化文档,它面向设计开发人员,详细描述了各个用例内部的基本信息、执行流程及例外流程等,必要时可附上相应的活动图。因为用例规约是设计和开发的重要参考文档,所以在编写过程中务必要做到详尽和准确。
下面仅给出“注册”这个用例的用例规约作为示例和参考。
必要时,可在下端附上活动图:
总结
在每个迭代周期的初始阶段,当选择完需要迭代的特性后,就可以开始领域分析和需求分析了。本文是先进行领域分析。其实两者并无确定的先后顺序,往往是相辅相成,交叉进行。下一篇文章将进入第一迭代周期的设计阶段。