谈谈领域模型的那些事儿 之 从领域获取知识

[b]前言:[/b]你写过用例模型吗?也许有;你写过领域模型吗?也许还没有。在这里,我们可以尝试写写领域模型,看看它的作用、带给我们的好处。

随着RUP在中国的传播,人们开始尝试用RUP统一过程来指导软件的设计和开发,但这些尝试并不成功。比较普遍的,大家都开始使用用例模型来进行需求阶段的分析和设计了。当然,能做出第一步已经非常不错了,但这远远不够。要做好需求分析,用例模型可以帮助我们分析清楚软件需求中要求的各个流程,但我们还缺少OO分析。过去,一旦需求分析完成以后,经过简短的分析过程,马上就开始进入开发阶段。在开发阶段,应当设计哪些类,它们的职责是什么,应当为它们设计哪些属性和方法,如何协作工作,在何时由谁来创建,以上这些问题都没有经过系统的分析,而是非常随意地添加类,非常随意地添加属性和方法,并且非常随意地在某个时刻创建对象。这样的设计,虽然可以实现需求所要求的功能,但它必然不能低耦合、高内聚,实现一个灵活多变、可维护性高的系统。即使有个别有经验的程序员的灵光闪现,但那只能在某个局部得到了优化,从整体上依然不是一个理想的分析设计。总之,没有经过系统的OO分析和设计,我们不能提高我们的代码质量。要进行系统的OO分析和设计,在需求分析阶段就要从领域模型开始。

从严格意义上讲,领域模型还不算是真正的OO分析和设计。真正的OO分析和设计是在需求分析人员完成需求分析以后,由技术人员完成的分析和设计(即分析模型和设计模型)。但领域模型在为日后的分析与设计准备着素材。用例模型为日后的分析与设计准备着流程操作方面的素材(动态模型),而领域模型为日后的分析与设计准备着类与类之间关系方面的素材(静态模型)。领域模型就是描述日后可能关心所有事物(可能描述成类,也可能描述成类中的属性)以及它们之间的关系。因此,领域模型是一批类图,以及它们的说明。它与用例模型在需求分析阶段一起进行编写,不分先后顺序,并在完成该阶段以后作为工作成果一起提交。

和用例模型一样,领域模型的建立也是以迭代的方式逐渐推进的。但一直以来,对领域模型设计进行描述的书籍比较少,即使在大师Craig Larman的经典著作《UML和模型应用》中对领域模型的也比较模糊。然而,这一现状被另一位大师Eric Evans打破,在他的经典著作《领域驱动设计》中,详细为我们描述了如何通过领域模型,驱动我们进行OO分析和设计。下面我们开始我们的领域驱动之旅吧。

[b]从业务领域提取知识[/b]

在一个阳光明媚的下午,我们一个个西装革履、精神抖擞地来到了客户的办公现场。在一个明亮的会议室里,宽大深褐色的椭圆木桌旁已经聚集了十来个业务人员。看到我们进来,大家握手问候。相继就座后,互相介绍,往来寒暄,唠唠家常。共同的家乡,或熟或不怎么熟的某个人,都可能成为拉近彼此关系的理由。逐渐,一切开始进入正题。客户开始絮絮叨叨的描述自己的需求,而我们则在紧张的做着记录,是不是问一些问题,表明我们的立场,抒发我们的建议。在这样一个过程中,客户会描述他们的每一个业务,会讲解每个业务的流程,他们会讲出一些业务领域的专业词汇(尽管有些你当时还不太懂)。在这样一个过程中,作为需求分析员,你应当非常注意业务流程中的一些关键词汇,你应当(在当时或者过后)将它们提取出来,通过询问客户,弄清楚他们的定义,以及相互之间的关系。而这些词汇就是建立领域模型的开始。

这是一个财务软件的业务讨论会,一个业务人员正在跟我讲付款单是怎样制作成凭证的。“每张付款单都有一个商品明细,每个商品明细都有它的价格、数量和金额。”他指着一张付款单向我解释着。从这句话,我可以提出一些关键信息:付款单、商品明细、价格、数量和金额。付款单与商品明细是一对多关系,并且商品明细聚合在付款单中。每个商品明细都有价格、数量和金额,也就是说,价格、数量和金额是商品明细的属性,这都很清楚。紧接着,他下面的讲解就不是那么清楚容易了。“如果按照一张单据生成一张凭证,那么每张付款单生成一张凭证。单据中的每个明细在凭证中生成一条借方分录和一条贷方分录。将付款单中的付款科目作为借方科目,将付款单结算方式对应的结算方式科目作为贷方科目。现结的付款单在采购发票中已制作凭证了,因此不再单独制作凭证。非预付的付款单不制作凭证,而是其执行付款核销以后,在核销单中制作凭证。”经过对以上语言的分析,我们可以绘制以下关系:一张凭证包含多个分录,是内聚关系。分录分为借方分录和贷方分录两种。一条商品明细对应一条借方分录和一条贷方分录。借方分录中包含“借方科目”属性,对应付款单中的付款科目;贷方分录中包含“贷方科目”属性,对应的是付款单中的一个什么科目。在这里,你可能对客户的描述不明白,因此要他做出解释。原来客户预先制订了一个规则,付款单中的结算方式分布对应了一个结算方式科目。OK,你在绘制的图形中,把结算方式科目作为关联类,将结算方式和贷方科目进行了一个关联。这样,“付款单生成凭证”这样一个场景的领域模型就绘制出来(如图)。
[img]http://dl.iteye.com/upload/attachment/615860/c5b40dcb-c9b8-3cca-87c8-aeebb7df6d8f.jpg[/img]

[b]1.如何提取概念类[/b]
这是一个我们非常熟悉的情景剧,它为我们揭示了建立领域模型是怎样开始,也就是说,我们是如何获取领域模型所需素材的。获取领域模型所需素材通常有两个途径:与客户现场交流中获得,和在用例的各个流程中提取名词或名称短语获得,这些我们称之为概念类。现在的问题是,哪些应当成为领域模型中的概念类呢?如果我引用一堆定义和准则,并不能让你清楚明了,也许一个生动的比喻更能够让你理解深刻。需求分析有时候就像一部部动画剧,而那些枯燥乏味的概念,纷繁复杂的流程,在这些动画剧中似乎都突然活了,个个都有语言有性格。在这些动画剧中扮演的所有角色,就是我们需要的概念类。而他们做的所有动作,就是用例模型中的所有流程。

另一个比较挠头的问题就是,业务领域中的哪些概念应当成为概念类,哪些应对成为概念类中的属性。这是一个非常模糊的问题,没有一个准确的答案。一般来说,如果一个概念记录的仅仅是一段文字或一个数字,那么它应当作为一个概念类中的某个属性,否则就应当作为一个概念类。比如“快递员送快递”,这里的“快递员”、“快递”都是从中提取的概念类,但是“快递”中的“地址”呢?这要看你现在分析的这个系统如何去记录这个“地址”了。如果记录的仅仅是一个文本,那么它应当成为“快递”中的一个“地址”属性;如果记录的不仅仅是一个文本,而是精细地记录了“邮政编码”、“城市”、“街道”、“通讯地址”等信息,那么它应当成为一个概念类,与“快递”进行关联。

除了这些名称和名称短语形成的概念类,还有一些相对独立的行为,作为服务也应当形成概念类。这一类概念类我们可以在需求分析不断深化的过程中,在以后的迭代中加入到领域模型。

[b]2.建立关联关系[/b]
除了提取概念类,领域模型还需要绘制出这些概念类相互之间的关系。由于领域模型是一个类图,概念类是一个个的类,因此概念类之间的关系一般有三种:依赖、关联和继承。

依赖是类与类之间最普通的关系,它仅仅表示一个类(客户类)了解它的供应者类,并且供应者类的变化会影响到客户元素。依赖关系表现为,客户类会创建、引用供应者类,或者供应者类是客户类中的一个变量(参数变量、局部变量、全局变量或属性变量)。没有供应者类将会造成客户类无法创建、无法使用,因此依赖是一种耦合关系。依赖在UML中被绘制成从客户类指向供应者类的一条虚线箭头。

关联是依赖关系的一种特例,它代表供应者类是客户类的一个属性变量。关联关系往往具有双向性,如一个部门关系的关联中,部门是员工类的一个属性,表示某个员工的所属于一个部门,而员工集合又是部门类的一个属性,表示某个部门下都有哪些员工。但是,在我们研究的业务领域中,我们往往关心的是某个方向的关联关系而忽视了另一个方向的关联关系。如一个工资管理系统中,我们往往关心的是员工在哪个部门,而很少关心某个部门下有哪些人。在这种情况下,关联关系表现为了一种导航,即员工对部门的导航。关联关系在UML中表示成一条直线,而具有导航的关联关系则表示成从被引用类指向引用类的实线箭头,即员工指向部门的箭头。

除了导航,关联关系还表现为一对一、一对多、多对一和多对多关系。在绘制关联关系的时候,我们通过在实线的两端标注“1”或“*”来说明,这里就不展开讨论了。另外,我们还可以在关联关系的中间用简短的一个词或短语,说明这是怎样的一个关系,或者在关联关系的两端标注这两个类分别代表的角色。

特别值得说明的是多对多关系。为了说明多对多关系的对应关系,往往还需要在关联关系中添加一个关联类。如用户权限模块的用户与角色就是一个多对多关联,因此在它们中间还需要增加一个“用户与角色关系”类,表明哪些用户与角色关联,哪些角色与用户关联。
关联关系是领域模型主要关注的一种关系。

聚合关系是关联关系的特例,它除了表示一种具有导航的关联关系外,还表示一种整体与部分的关系,如订单与订单明细、凭证与凭证分录等。聚合表示为一段是黑色菱形的实线,黑色菱形端代表整体,另一端代表部分。聚合在以后的领域分析中占有重要位置,我们回头再讲。组合是聚合的特例,它与聚合的唯一区别是,当代表整体的类被摧毁时,代表部分的类必须摧毁。由于组合涉及到了太多的技术实现,与领域模型的宗旨不符,我们往往很少去分析组合关系。

除此之外,领域模型还会出现继承关系。由于领域模型中不可能出现接口,因此领域模型不可能出现实现关系。

[b]3.领域模型的说明[/b]
在建立领域模型的时候一个非常重要的事情就是,一定要避免领域模型中的概念类出现二义性。在一次我与客户讨论需求的过程中,我和客户都使用了一个业务术语,但我们对这个业务术语的理解存在着差异,以致我们花了大量时间来讨论一个问题,却谁也没有向对方说明白自己的意思,直到最后我们发现对这个术语理解的偏差。这是一个反面的例子,说明避免二义性对沟通的重要。领域模型的说明,应当对一些重要词汇或者业务术语进行必要解释。

另外,领域模型的说明还有详细解释各个类之间的相互关系,特别是那些关联关系,为日后的分析设计提供支持。

你可能感兴趣的:(设计模式)