第三章
领域建模与系统行为分析
用例模型原则上不是面向对象的,它描述的是系统的功能,只是建立系统的最初的输入,为了更细腻的分析需求,从面向对象的角度,可以建立领域模型。
识别一个丰富的对象集或者领域类集,是面向对象分析的核心工作,做好这项工作,将会在设计和实现期间获得丰富的回报。
第一节
领域建模的思想和方法
领域模型是作为设计软件对象的启发来源,也是后续工件的必须输入。
领域模型是说明问题域里(对建模者来说)有意义的
领域类,它是面向对象分序的时候要创建的最重要的工作(必须说明,用例虽然也是一个重要的分析工作,但它并不是面向对象的,它是强调的概念的过程视图)。
一、领域建模的思想及其方法学问题
什么是“问题域”和“领域建模”?
问题域
:
现实世界中系统所要解决问题的领域为“问题域”,如“银行业务”属于“银行的问题域”。
领域建模
:
1.我们设计一个系统,总是希望它能解决一些问题,这些问题总是会映射到现实问题和概念。
2,
对这些问题进行归纳、分析的过程就是领域建模(这个域,指的就是问题域)。
建立领域模型的好处:
1,通过建立领域模型能够从现实的问题域中找到最有代表性的概念对象
2,并发现出其中的类和类之间的关系,因为所捕捉出的类是反馈问题域本质内容的信息。
经典的面向对象的分析或调研的步骤,是把一个相关的领域,分解为单个领域类或者对象(是一个我们能够理解的概念)。
领域模型是领域类或者是我们感兴趣的现实对象的可视化表示。
它们也被称之为:概念模型、领域对象模型、分析对象模型等。
在UML中,领域模型是不定义操作(方法)的一组类图来说明,它主要表达:
1, 领域对象或者领域类
2, 领域类之间的关联
3, 领域类的属性
属性用以表达对象的状态。
(
1
)三种领域类
1
,边界对象:参与者使用该对象与系统进行交流,也即边界对象代表系统的内部工作和它所处环境之间的交互。
边界对象将系统的其它部分和外部的相关事物隔离和保护起来。其主要的责任是:输入、输出和过滤。
2
,实体对象:代表要保存到持续存储体中的信息。实体类通常用业务域中的术语命名。
通过它可以表达和管理系统中的信息。在模型中,系统中的关键概念以实体对象来表现。其主要的责任是:业务行为的主要承载体
3
,控制对象:它协调其他类的工作,每个用例通常有一个控制类,控制用例中的时间顺序。
它可能是与其它对象协作以实现用例的行为,控制类也称管理类。其主要的责任:控制事件流,负责为实体类分配责任
有四个规则对应上面的三种分析类对象间的交互
1,用例的参与者只能与边界对象交互(这相当于结构化分析里面的自动化边界)
2,边界对象只能与控制对象和动作者交互(即不能直接访问实体对象)
3,实体对象只能与控制对象交互
4,控制对象可以和边界对象交互,也可以和实体交互,但是不能和动作者交互
三种领域类的UML的图示如下:
(2
)领域建模的简单例子
下面举个简单的例子,说明领域建模的基本概念。
1
)问题的描述
例如:两个领域类Payment(支付)Sale(售出)在领域模型中以一种有意义的方式关联。
2
)关键概念
仔细考察上面的图,可以看出,领域模型实际上是可视化了领域中的单词或领域类,并且为这些单词建立了领域类。
也就是说,领域模型是抽象了一个可视化字典。
模型展现了部分视图或抽象,而忽略了建模者不感兴趣的细节。
它充分利用了人类的特点—大脑善于可视化思维。
3
)领域模型不是软件组件的模型
领域模型视相关现实世界领域中事务的可视化表示,不是Java或者C#类这样的软件组件。
下面这些元素不适合在领域模型中表述:
1,软件工件(窗口或数据库)
2,职责或者方法:方法是个纯粹的软件概念,在设计工作期间考虑对象职责是非常重要的,但领域模型不考虑这些问题,在这里考虑职责的正确方法是,给对象分配角色(比如收银员)。
4
)领域类
领域模型表示领域中的领域类或词汇,一个不是太准确的描述:一个领域类就是一个观点、事务或者对象。
比较准确的表达:
领域类可以按照它的符号、内涵和外延来考虑。
l 符号:代表一个领域类的单词或者图片。
l 内涵:领域类的定义。
l 外延:领域类定义的一组实例。
二、领域类的识别
我们的目标是在相关领域中创建有意义的领域类。
比如说创建“处理销售”用例中的相关领域类。
一般来说,用大量细粒度的领域类来充分描述领域模型,比粗略描述要好。
下面是识别领域类的一些指导原则:
l 不要认为领域模型中领域类越少越好,情况往往恰恰相反。
l 在初始识别阶段往往会漏掉一些领域类,在后面考虑属性和关联的时候才会发现它,这是应该把它加上。
l 不要仅仅因为需求中没有要求保留一些领域类的信息,或者因为领域类没有属性,就排除掉这个领域类。
l 无属性的领域类,或者在问题域里面仅仅担当行为的角色,而非信息的角色的领域类,都可以是有效的领域类。
1
)识别领域类的策略
下面提供了两种识别领域类的技巧。
1. 使用领域类分类列表。
2. 识别名词短语。
2
)使用领域类分类列表
通过建立一个候选的领域类的列表,来开始建立模型。下面是一个从商店和航空订票领域中抽取出来的概念列表(注意,排列不考虑重要性)。
领域类分类
|
示例
|
物理或具体对象
|
(略)
|
事物的设计、描述、或规范
|
|
位置
|
|
交易
|
|
交易项目
|
|
人的角色
|
|
其它事物的容器
|
|
容器包含的元素
|
|
在该计算机之外的其它计算机或电子机械系统
|
|
抽象名词的概念
|
|
组织
|
|
过程(通常不表示一个概念,但可以被表示成一个概念)
|
|
规则和政策
|
|
分类
|
|
有关工作、契约和法律事务的记录
|
|
财务设施及服务
|
|
手册、文档、引用论文、书籍
|
|
3
)根据名词短语识别找出领域类
曾经有人提出了用名词短语分析找出领域类的方法,然后把它们作为候选的领域类或者属性。使用这种方法必须十分小心,从名词机械的映射肯定是不行的,而且自然语言中的单词本来就是模棱两可的。
不过,这仍然是灵感的另一种来源,比如,我们来看一看原来写出来的“处理销售”的用例:
基本流程:
1.顾客携带购买的商品到达POS机收费口
2.收银员开始一次新的销售
3.收银员输入商品标识
4.…..
重复 3 – 4 步,直到结束。
5.………
…….
10.顾客携带商品和收据离开
|
仔细研究其中的名词,可以看到很多有用的领域类(“记账”、“提成”),也可能有些是属性,请研究我们后面要讨论的关于区分属性的和类的讨论。
这种方法的缺点就是不精确,但对我们研究问题会非常有用。
推荐:
把领域类分类,和词语分析一起使用。
三、领域建模的指导原则
1
)事物的命名和建模
领域模型是问题域中的概念或这是事物的地图,所以地图绘制员的策略,也适用于领域模型的建模。
l 使用地域中已有的地名(和城市名相同)
l 排除不相关的特性(比如居民人数)
l 不添加不属于某个地方的事物(比如虚构的山川)
以此,我们建议使用如下的原则:
l 给领域模型建模,要使用问题域中的词汇。
l 把和当前不相关的领域类排除在问题域之外。
l 领域模型应该排除不在当前考虑下的问题域中的事物。
2
)在识别领域类的时候一个常犯的错误
在建立领域模型的时候,最常犯的一个错误就是把原本是类的事物当作属性来处理。
Store(商店)是Sale(出售)的一个属性呢?还是单独的领域类Store?
大部分的属性有一个特征,就是它的性质是数字或者文本。
而商店不是数字和文本,所以Store应该是个类。
另一个例子:
考虑一下飞机订票的问题,Destination(目的地)应该是Flight(航班)的属性呢还是一个单独的类Airport(包括属性name)。
在现实世界中,目的地机场并不是数字和文本,它是一个占地面积很大的事物,所以应该是个领域类。
建议:
如果我们举棋不定,最好把这样的事物当做一个单独的领域类,因为领域模型中,属性非常少见。
四、分析相似的领域类
有一些情况是比较不太容易处理的。
举个例子,我们来分析一下“Register(记录)”和“POST(终端)”这两个概念。
POST作为一个销售终端,可以是客户端任何终点的设备(用户PC,无线PDA),但早期商店是需要一个设备来记录(Register)销售。
而POST实际上也需要这个能力。
可见,Register是一个更具抽象性的概念,在领域模型中,是不是应该用Register而不是POST吗?
我们应该知道,领域模型其实没有绝对正确和错误之分,只有可用性大小的区分。
根据绘图员原则,POST是一个领域中常见的术语,从熟悉和传递信息的角度,POST是一个有用的符号。
但是,从模型的抽象和软件实现相互独立的目标来看,Register是一个更具吸引力和可用性的表达,它可以方便的表达记录销售位置的概念,也可以表达不同的终端设备(如POST)。
两种方式各具优点,关键是看你的领域类重点是表达什么信息。
这也是一个架构师必备的能力—抓住重点。
五、为非现实世界建模
一些业务领域有自己独特的概念,只要这些概念是在业内被认可的,同样可以创建领域类,比如在电信业可以建立这样的领域类:
消息(Message)、连接(Connection)、端口(Port)、对话(Dialog)、路由(Route)、协议(Protocol)等。
六、规格说明或者描述领域类
在领域模型中,对领域类作规格说明的需求是相当普遍的,因此它值得我们来强调。
假定有下面的情形:
l 一个Item实例代表商店中一个实际存在的商品
l 一个Item表达一个实际存在的商品,它有价格,ID两个描述信息
l 每次卖掉一个商品,就从软件中删掉一个实例。
如果我们是这样来表达:
那很可能会认为随着商品的卖出,它的价格也删掉了,显然这是不对的。
比较好的表达方式是这样的:
1
)何时需要规格说明类
在下面的情况下,需要添加领域类的说明类:
l 商品或服务的信息描述,独立于商品或者服务当前已经存在的任何实例。
l 删除所描述的事物,会导致维护信息的丢失。
l 希望减少冗余或者重复的信息。
2
)服务的描述
作为领域类的实例可以是一次服务而不是一件商品,比如航空公司的航班服务。
假定航空公司由于事故取消了6个月的航班,这时它对应的Flight(航班)软件对象也在计算机中删除了,那么,航空公司就不再有航班记录了。
所以比较好的办法是添加一个FlightDestination(航班目的)的规格描述类,请看下面的例子。
七、统一过程中的领域模型
根据“
统一过程的时间及其时间安排”的那张表,在UP中,通常在细化阶段开始并完成领域模型的建模。事实上,对于一个有经验的系统构架师,在每次迭代里开发领域模型,只需要几个小时就够了。
在UP中,有一个业务对象模型(BOM),但实际上并不通用,而领域模型实际上是BOM的一个正是的变体。在RUP中,BOM是这样来定义的:它是业务员和业务实体如何相关联,以及为了完成业务如何写作的抽象。
BOM可以用多种不同的图(类图、活动图、顺序图)来表示,这些图说明整个企业应该如何运行。
不过对于单个软件的应用,这似乎是个不太通用的活动。尽管它还有一些变通,但是在系统架构设计中,领域模型仍然是最为广泛的被采用的。
第二节
领域模型的关联
一、找出关联
关联,是类(事实上是实例)指示有意义或相关连接的一种关系。
关联事实上表示是一种“知道”。
如果不写箭头,关联的方向一般是“从上到下,从左到右”。
我们可以使用下面的表来找出关联
分类
|
示例
|
A在物理上是B的一部分
|
(略)
|
A在逻辑上是B的一部分
|
|
A在物理上包含在B中/依赖于B
|
|
A在逻辑上包含在B中
|
|
A是对B的描述
|
|
A是交易或者报表B中的一项
|
|
A为B所知道/为B所记录/为B所扑获
|
|
A是B的一个成员
|
|
A是B的一个组织子单元
|
|
A使用或者管理B
|
|
A与B通信
|
|
A与一个交易B有关
|
|
A是一个与另一个B有关的事物
|
|
A与B相邻
|
|
A为B所拥有
|
|
A是一个与B有关的事件
|
|
二、关联的指导原则
l 把注意力集中在那些需要把概念之间的关系信息保持一段时间的关联(“需要知道”型关联)。
l 太多的关联不但不能有效的表示领域模型,分而会使领域模型变的混乱,有的时候发现某些关联很费时间,但带来的好处并不大。
l 避免显示冗余或者导出的关联。
三、角色和多重性
关联的每一端称之为“角色”。
角色可选的具有:
名称;
多重性表达式;
导航性。
多重性
多重性表示一个实例,在一个特定的时刻,而不是一段时间内,可以和多个实例发生关联。
“*”表示多个。
“1”表示一个。
“0..1”表示1或者没有,比如一个商品在货架上,可能售出,也可能被丢掉了,这种情况,用“0..1”是合理的。问题是我们需要关心这样的观点吗?如果是数据库,可能表达这个数据存在,或者损坏。但在领域模型并不表示软件对象,通常我们只对我们有兴趣的内容建模,从这个观点出发,也可能只有“1”或者“*”是合理的。
再一次提醒,发现领域类比发现关联更重要,花费在领域模型创建的大部分时间,应该被用于发现领域类,而不是关联。
四、两种类型之间的多重关联
两种类型之间的多重关联是可能存在的。
比如航空公司的例子,Flight-to和Flight-from可能会同时存在,应该把它们都标出来。
第三节
领域模型的属性
发现和识别领域类的属性,是很有意义的。
属性是个逻辑对象的值。
属性主要用于保留对象的状态。
一、有效的属性类型
大部分属性应该是简单数据类型。
当然也可以使其它的一些必要的类型,比如:Color(颜色)、Address(地址)、PhoneNumber(电话号码)等。
二、非原始的数据类型类
在领域类中,可以把原始数据类型改成非原始数据类型,请应用下面的指导原则:
l 由分开的段组成数据(电话号码,人名)
l 有些操作和它的数据有关,如分析和验证等(社会安全号码)
l 包含其它属性的数据(促销价格的开始和结束时间)
l 带有单位的数据值(支付金额有一个货币单位)
l 对带有上述性质的一个或多个抽象(商品条目标识符)
如果属性是一个数据类型,应该显示在属性框里面。
第四节
泛化建模
泛化和特化是概念建模的基本概念,另外,领域类的层次,往往是软件类层次的基本源泉,软件类可以利用继承来减少代码的重复。
一、领域模型的概念提取
UP的领域模型,是在不断考虑迭代需求的相关概念的过程中发展起来的。
很多人对概念建模都有一些细腻的建模问题的讨论。比如,有一个有一定作用的概念,就是概念分类表(Concept Category List)。
1
)概念分类表
本次迭代所涉及的一些显著概念,可以列出一个表来。
类别
|
示例
|
物理或者实际的对象
|
CreditCard(信用卡),Check(支票)
|
事物的说明、设计或描述
|
|
位置
|
|
交易
|
CashPayment(现金支付)
CreditPayment(信用卡支付)
Check Payment(支票支付)
|
交易的项目
|
|
人的角色
|
|
其它事物的容器
|
|
容器中的事物
|
|
系统之外其它计算机或电子机械系统
|
CreditAuthorizationService(信用卡支付授权服务)
CheckAuthorizationService(支票支付授权服务)
|
抽象名词概念
|
|
组织
|
CreditAuthorizationService(信用卡支付授权服务)
CheckAuthorizationService(支票支付授权服务)
|
事件
|
|
规则及策略
|
|
目录
|
|
财务、工作、合约、法律事务的记录
|
AccountsReceivable账目接受
|
金融工作和服务
|
|
手册、书籍
|
|
2
)从用例中得到名词对照的概念
再次重申,不能机械的用名词与概念的对照来识别领域模型的有关概念。由于自然语言的模棱两可,文本中的相关概念并不总是明确和清晰的,因此我们必须判断并作合适的抽象处理。不过,由于名词和概念对照的直观性,它仍然是概念建模的一个实用技术。
每一次迭代,实际上都要反过来研究用例并进行需求分析,利用需求驱动项目进一步精化。在这次迭代中,我们将处理销售过程(Process Sale)用例的信用卡和支票的支付场景,下面显示扩展场景中一些名词对照的概念。
用例1
:Process Sale
|
……
扩展:
7b. 信用卡支付
1. 顾客输入
信用卡账号信息。
2. 系统向外部
信用卡支付授权服务系统发出
授权支付请求和
批准支付的请求
2a. 系统检测到和外部信用卡授权服务系统通信故障
1.系统通知收银员发生了错误
2.收银员向客户请求更换支付方式
3. 系统收到
支付批准并通知收银员
3a. 系统收到
支付拒绝的通知
1.系统通知收银员系统拒绝支付
2.收银员要求顾客改变支付方式
4.系统记录
信用卡的支付情况,包括支付授权
5.系统出示信用卡签名的输入方式
6.收银员要求顾客为信用卡支付签名,顾客签名
7c. 用支票支付
1. 顾客签发
支票,将
支票和
驾驶执照一起交收银员
2. 收银员把驾驶执照号码记录在支票上,将其输入,发出
支票支付授权请求。
3. 生成一个
支票支付请求,将其发送到外部的
支票授权系统。
4. 系统收到支票支付授权并通知收银员。
5. 系统记录
支票的支付情况,其中包括
支付批准。
…………
|
其中,粗体的就是我们发现的概念名词。
3
)授权服务的交易
由名词对照,我们还可以发掘出诸如CreditPaymentRequest(信用卡付款请求)以及 CreditApprovalReply(信用卡批准应答)这样一类概念,这些概念都可以视为外部服务的不同类型的交易。
一般来说,识别这些交易非常有用,因为许多活动和处理都是围绕交易而进行的。
这些交易概念,不需要代表计算机中的记录,也不需要代表连线上的比特数据,它们代表了独立于执行方式的交易的抽象。
比如,信用卡支付方式可以通过打电话或者计算机发出请求来完成。
二、泛化及其应用
CashPayment(现金支付)、CreditPayment(信用卡支付)和Check Payment(支票支付)这几个概念非常接近,可以组织成一个泛化的类层次,其中超类Payment(支付)具有更普遍的概念,而子类是一个更具体的概念。
注意,这里讨论的是领域类,而不是软件类。
泛化(generalization)是在多个概念之间识别共性,定义超类和子类关系的活动,它是构件概念分类的一种方式,并且在类的层次中得到说明。
在领域模型中,识别超类和子类及其有价值,因为通过它们,我们就可以用更普遍、更细化和更抽象的方式来理解概念。从而使概念的表达简约,减少概念信息的重复。
三、定义概念性超类和子类
由于识别概念性的超类和子类具有价值,因此根据类定义和类集,准确的理解泛化、超类、子类是很有意义的,下面我们将讨论这些概念。
1
)泛化和概念性类的定义
定义:
一个概念性超类的定义,比一个概念性子类的定义更为普遍或者范围更广。
在前面的例子中,Payment(支付),是一个比具体的支付方法更为普遍的定义。
2
)泛化与类集
概念性子类与概念性超类,在集的关系上是相关的。
所有概念性子类集的成员,都是它们超类集的成员。
3
)概念性子类定义的一致性
一旦创建了类的层次,有关超类的声明也将适用于子类。
一般的说,子类和超类一致是一个“100%规则”,这种一致包括“属性”和“关联”。
4
)概念性子类集的一致性
一个概念性子类应该是超类集中的一个成员。
通俗的讲,概念性子类是超类的一种类型(is a kind of),这种表达也可以简称为is-a。
这种一致性称之为Is-a规则。
所以,这样的陈述是可以的:
“信用卡支付是一个支付”(CreditPayment is a Payment)。
5
)什么是正确的概念性子类呢
从上面的讨论,我们可以使用下面的测试,来定义一个正确的子类:
l 100%规则(定义的一致性)
l is-a规则(集合成员关系的一致性)
6
)何时定义一个概念性子类
举个例子,把顾客(Customer)划分为男顾客(MaleCustomer)和女顾客(FemaleCustomer),从正确性来说是可以的,但这样划分有意义吗?
这样划分是没有意义的,因此我们必须讨论动机问题。
把一个领域类划分为不同子类的强烈动机为:
当满足如下条件之一的时候,为超类创建一个概念性的子类:
l 子类具有额外的相关属性。
l 子类具有额外的相关关联。
l 子类在运行、处理、反应或者操作等相关方式上,与超类或者其它子类不同。
l 子类代表一个活动的事务(例如:动物、机器人),它们与超类的其它子类在相关的行为方式上也不同。
由此看来,把顾客(Customer)划分为男顾客(MaleCustomer)和女顾客(FemaleCustomer)是不恰当的,因为它们没有额外的属性和关联,在运行(服务)方式上也没什么不同。
尽管男人和女人的购物习惯不同,但对当前的用例来说不相关。这就是说,规则必须和我们研究的问题相结合。
7
)何时定义一个概念性超类
在多个潜在的子类之间,一旦发现共同特征,就可以暗示可以泛化得到一个超类。
下面是泛化和定义超类的动机:
l 潜在的概念子类代表一个相似概念的变体。
l 子类遵守100%的is-a规则。
l 所有的子类具有共同的属性,可以提取出来并在超类中表示。
l 所有子类具有相同关联,可以提取并与超类相关。
8
)发现领域类的实例
Payment类:
授权服务类:
注意,在构造超类的时候,层次不宜太多,关键是表达清晰。
事实上,额外的泛化不会增加明显的价值,相反带来很大的负面影响,没有带来好处的复杂性是不可取的。
四、抽象领域类
在领域模型里面,识别抽象类是有用的,因为它们限制了哪些类可能具有具体的实例。
如果一个类的成员,必须是它子类的成员,那么称它为抽象领域类。
在上面的例子里,Payment必须用更具体的CreditPayment、CheckPayment做实例,而Payment本身并不能实例化,所以Payment是一个抽象的领域类。
第五节
精化领域建模及若干难以确定的要素
在前面讨论的基础上,我们需要把领域模型进一步的精化。
一、关联类
下面的概念需求,需要考虑关联类的问题:
l 在通讯过程中,授权服务为每个商店分配一个店主ID。
l 从商店发送到授权服务的支付请求,需要店主的ID以标识这个商店。
l 进一步,对每一个授权服务,每一个商店都有一个不同的店主ID。
在UP的概念建模中,商店的ID的属性到地方在什么地方呢?
把merchantID(店主ID)置于Store(商店)类中是不正确的,因为一个Store类会有多个merchantID值。
注意,属性表示了对象的状态,因此,一个对象的属性,同时只能有一个值。
把merchantID置于AuthorizationService(授权服务)类中也是不恰当的,因为它同样也有多个值。
通过上面的讨论,我们就可以得出下面的建模原则:
在概念建模中,如果一个类C的属性A 同时具有多个值,那么不要把属性A放到类C中,而是把属性A 放到与C关联的关联类里面去。
出现下面的情况可以在领域模型中加入关联类:
l 一个属性和关联有关。
l 关联类的生命周期依赖于关联。
l 在两个概念之间存在多对多的关联或者关联本身具有某些待表示的信息。
下面的例子,表达一个人可能受雇于多个公司。
二、聚集和组合
聚集(Aggregation)是用于整体—部分关系建模的一种关联,整体称之为
组合(composite)。
1
)组合聚集
组和聚集用实心菱形表达,原来的意义,它表达部分的存在依赖于组合。
比如:一只手和手指,手指的存在依赖于手。
在设计模型中,组合的含义很清楚,它表达某一个对象的生命周期,依赖于另一个对象(全局性的定义,并且在构造的同时实例化)。
但是在领域模型中,这种整体的创建对部分的影响并不太清楚(一个实际的销售并不可能创建实际的销售商品),所以,花功夫发现这种关系并没有什么实际意义。
2
)共享聚集
共享聚集的图是空心菱形,它表示组合末端(菱形处)的多重性可能大于一。
它的含义是,部分可能同时属于多个组合实例。
在设计模型中,这种共享聚集和关联有几乎相同的实现方法(全局性的定义,但对象构造的时机待定)。
但是,在实际的物理集合中,很少能找到相似的例子。
注意,发现和使用聚集在设计模型中相当有意义,但与设计模型不同,在领域模型中识别和说明聚集并不会产生深远影响。
学院派的建模理论花了很多时间来讨论这些关联上的细腻差别,但是,很多经验丰富的建模者最终发现,它们在关联的细微含义上浪费了太多的无谓的时间。
所以,在领域模型的关联问题上,我们将排除这种表示方式。
三、
案例:订单处理子系统
我们还是以网上电源设备销售子系统的案例,来讨论领域建模的过程。
领域建模的目的,是用类来表达一个对象集,如果这个类是长久存在的(或者说是持久的)一个业务实体,比如,Customer、Order、Shipment等等,这样的类通常被称为
实体类。实体类往往代表着一个数据库的一个持久化对象,所以,这里的领域实体模型的建立,直接影响到数据库设计。
实体类定义了任何信息系统的本质,所以需求分析上很大程度上是发现实体对象。不过,从功能上来说,发现其它的功能类也是必要的。
有时候这样类的建模一直需要延续到设计阶段。
1
)从需求中寻找类
下面我们从功能需求的表中找到这个类。
编号
|
需求
|
实体类
|
1
|
客户使用制造商的Web页面查看所选择的
电源设备标配,同时显示价格。
|
Customer
客户
ES: StandardConfiguration 标准配置
Product
产品
|
2
|
客户查看配置细节,可以更改配置,同时计算价格。
|
Customer 客户
ConfiguredES: ConfiguredProduct
配置的电源:配置的产品
ConfigurationItem 配置项目
|
3
|
客户选择订购,也可以要求
销售人员在
订单真正发出之前和自己联系,解释有关细节。
|
Customer
客户
ConfiguredES 配置的电源
Order
订单
Salesperson
销售人员
|
4
|
要发出订单,
客户必须填写表格,包括地址,付款细节(信用卡还是支票)等。
|
Customer
客户
Order
订单
Salesperson 销售人员
Shipment
出货
Invoice
发票
Payment
付款
|
5
|
客户订单送到系统之后,
销售人员发送电子请求到
仓库,并且附上配置细节。
|
Customer
客户
Order
订单
Salesperson
销售人员
ConfiguredES
配置的电源
ConfigurationItem 配置项目
|
6
|
事务的细节(包括订单号和客户帐户号),
销售人员要e-mail给客户,使得
客户可以在线查询订单状态。
|
Customer
客户
Order
订单
OrderStatus 订购状况
|
7
|
仓库从
销售人员处获取发票,并且向客户运送电源设备
|
Invoice
发票
Shipment
出货
|
寻找领域类是一个迭代式的任务,需要反复思索,也可以试着回答下面的问题:
这个概念是一个数据容器吗?
它有取不同值的不同属性吗?
它已经有实体对象了吗?
它在应用领域的范围之内吗?
针对这张表我们还可以思考很多问题,比如:
1,ConfiguredES(自己选择配置的电源)和Orde(订单)的区别到底在哪里?毕竟我们不打算存储选择配置的电源,除非它的订单被提交。
2,第4号和第7号需求中的Shipment(出货)的含义是一样的吗?可能不一样,如果我们知道了运送是仓库的责任,这个类是不是还需要呢?
3,ConfigurationItem (配置项目)为什么不能是ConfiguredES中的一组属性呢?
4,OrderStatus(订购状况)为什么不能是Order(订单)的一个属性呢?
5,Salesperson(销售人员)是一个类,还是Order(订单)还是Invoice(发票)中的一个属性呢?
回答这些问题是不容易的,这需要对需求进行深入的研究,假定我们研究的结果出现了下面这些类(其中Customer实际上是用例中的参与者,所以是从用例的角度出现的)。
2
)确定属性
我们可以进一步定义一些属性,这些属性首先被想到的是原始类型的属性,其实定义属性确实是有很大的随意性的,这需要一些经验。
3
)发现关联
我们可以根据需求的理解,来发现一些关联。
但是在确定多重性的时候,可以作一些假定:
订单来自于单个客户,而客户可以提交多个订单。
订单除非在付款已经被说明之后才被接受,因而是一对一关联。
订单不一定要有一个所关联的发票,但发票总是和单个订单所联系。
一个订单是为一个或者多个自配置电源建立的,一个自配置电源可以被订购多次或者一次也没有。
这样就可以画出关联来。
4
)发现聚集
聚集是关联更强的形式出现的,不过事实上并不一定需要刻意的发现聚集,这里用聚集表达主要是为了强调这种关联的重要。
一个电源具备一个或者多个配置项目。
同样,一个自定义配置的电源也具有一个或者多个配置项目。
5
)泛化建模
面向对象的分析很重视泛化建模,这样一来可以大大简化和清晰化所建立的模型。
在这里,电源(ES)变成了一个更抽象的类,两个子类为“标准配置电源”和“自定义配置的电源”。
6
)包括属性的最后结果
第六节
系统行为分析中必须关注的问题
对一个软件应用程序进行逻辑设计之前,对系统进行研究,并把它的行为当作“黑箱”来考虑是有益的。
系统行为描述一个系统做什么,而不解释系统如何做。描述系统行为一部分是靠顺序图,另外一部分是靠用例和系统契约(以后会加以讨论)。
交互视图包括顺序图(Sequence Diagram)和合作图(Collaboration Diagram)和两种,主要解决描述对象之间的交互问题。
对象间的相互作用体现了对象的行为。
这种相互作用可以描述成两种互补的方式:
1)以独立的对象为中心进行考察;
2)以互相作用的一组对象为中心进行考察。
状态图的描述范围不宽,但它描述了对象深层次的行为,是单独考察每一个对象的“微缩”视图。
对状态图的说明是精确的并且可直接用于代码。
然而,在理解系统的整个功能时存在困难,因为状态图一个时刻只集中描述一个对象,要确定整个系统的行为必需同时结合多个状态图进行考察。
交互视图更适合于描述一组对象的整体行为。
交互视图是对象间协作关系的模型。
协作:
协作描述了在一定的语境中一组对象以及用以实现某些行为的这些对象间的相互作用。
它描述了为实现某种目的而相互合作的“对象社会”。
交互:
交互是协作中的一个消息集合,这些消息被类元角色通过关联角色交换。当协作在运行时,受类元角色约束的对象通过受关联角色约束的连接交换消息实例。交互作用可对操作的执行、用例或其他行为实体建模。
消息是两个对象之间的单路通信,从发送者到接收者的控制信息流。消息具有用于在对象间传值的参数。消息可以是信号(一种明确的、命名的、对象间的异步通信)或调用(具有返回控制机制的操作的同步调用)。
创建一个新的对象在模型中被表达成一个事件,这个事件由创建对象所引起并由对象所在的类本身所接受。
创建事件:作为从顶层初始状态出发的转换的当前事件。对于新实例是可行的。
消息可以被组织成顺序的控制线程。分离的线程代表并发的几个消息集合。线程间的同步通过不同线程间消息的约束建模。同步结构能够对分叉控制、结合控制和分支控制建模。
消息序列可以用两种图来表示:顺序图(突出消息的时间顺序)和协作图(突出交换消息的对象间的关系)。
一、从整体的角度研究系统行为
用例描述外部参与者与我们创建的软件之间如何交互。
交互期间,参与者产生一个发送给系统的事件,通常要求系统响应这个操作。
比如:
收银员输入一个商品的ID的时候,收银员将请求POS系统记录商品的销售,这个请求将触发一个系统的操作。
为了表达这些外部参与者与系统交互的过程,使用UML顺序图是可取的。
一个系统顺序图(SSD),是一个用来表示用例特定场景、外部参与者产生的事件。所有的系统都被当作黑箱,图的重点是从参与者跨越到边界的事件。
系统顺序图是把系统作为黑箱来设计。
顺序图将交互关系表示为一个二维图。纵向是时间轴,时间沿竖线向下延伸。横向轴代表了在协作中各独立对象的类元角色。类元角色用生命线表示。当对象存在时,角色用一条虚线表示,当对象的过程处于激活状态时,生命线是一个双道线。
消息用从一个对象的生命线到另一个对象生命线的箭头表示。箭头以时间顺序在图中从上到下排列。
后面会讨论顺序图也可以说明交互软件对象的设计问题。
1
)一个SSD
示例
下面的例子是用SSD显示参与者与系统(作为黑箱)直接的交互,参与者所产生的事件。
2
)SSD
和用例
SSD显示用例的一个场景中的系统事件,所以它产生自用例的考察。你可以直接把它和用例中的步骤对应起来。
3
)系统事件和系统边界
为了识别系统事件,必须象前面用例的讨论一样,清楚的定义系统的边界,由于软件开发的目的,系统边界经常被定义为软件本身,在这样的语境下,系统事件是直接激活软件的外部系统事件。
在上面处理销售的例子中,由于顾客并没有直接参与POS系统的交互,所以顾客不是系统事件的参与者,只有收银员才是。
4
)命名系统事件及操作
因为名称强调事件的命令导向,所以事件以动词开头(add、enter、end、make)可以增加清晰度。
比如:
enterItem(加入项目)比scan(激光扫描仪)要好。
5
)显示用例文本
事实上,这个顺序图是为了更清晰的表达问题,特别是为了更好的和用户交流,所以,很多情况下,希望能在顺序图中显示场景的用例片断,比如:
6
)SSD
和术语表
SSD中显示的术语(操作、参数、返回值)是简练的,有的时候可能需要适当的解释,就应该在术语表中表达。
不过,如果讨论是工件的创建而不是编码,又没有必要建立享用的术语比哦澳是个令人怀疑的事情,除非用途和决策支持标明这真正有价值,就是一个不必要的工作。
二、统一过程中的SSD
SSD使用例模型中的一部分(用例中交互的可视化),但是在UP 中并不认为一定要用SSD来描述。
不过,有的时候花个几分钟或半个小时创建SSD也不是完全没有用处。
一般的情况是:
初始:
在初始阶段一般不会使用SSD。
细化:
在细化阶段,大多数SSD可以被创建出来,用来识别系统事件的细节,以及澄清哪些是系统必须设计来处理的主要操作。
注意:
没有必要为所有场景创建SSD,一般只是为当前迭代所选择的场景创建SSD。
三、分析对象之间的行为
当需要更加细腻的分析的时候,可以用更详细的顺序图来分析。下图为带有异步消息的典型的顺序图,这是一个服务网点的存款服务的用例。
激活:
激活是过程的执行,包括它等待嵌套过程执行的时间。在顺序图中它用部分替换生命线的双道线表示。
当控制流程重新进入对象中的一个操作递归时,递归调用发生,但是第二个调用是与第一个调用分离的激活。同一个对象中的递归或嵌套调用用激活框的叠加表示。
下图为含有过程控制流的一个顺序图,包括一个递归调用和一个对象的创建。
带有激活的顺序图
四、案例:
订单处理子系统
交互图很多情况下是对于活动图的深入研究构建起来的,比如对于已经讨论的设备购置案例,我们仔细的研究活动图,对“显示当前配置”的活动作更加细腻的研究。
它牵涉到三个类:
ConfigurationWindow,这是一个界面类;
ES类,这是ConfiguredES类或者StandardES类。
ConfigurationItem类,这是一个配置项目类。
需要表达的交互操作关系如下:
外部参与者Customer先选择电源的配置,然后消息openNew()发送给一个界面类ConfigurationWindow,这个消息导致创建一个实例aConfWin。
对象aConfWin需要“显示它自己”以及配置数据,为了这个目的,它发送一个消息getConf()给ConfiguredES类或者StandardES类,并且实例化一个对象aComp。
对象aComp使用输出参数item_rec根据ConfigurationItem对象“组合它自己”,然后批量发送配置项到aConfWin。消息displayES的参数为item_recset。
现在对象aConfWin能显示自己了,对应的交互图如下。
对这样的序列图的研究,可以为相应的类提供方法打下基础,比如受影响的三个类可以构造相应的方法。
一般来说,可以为每个用例构造一个单独的序列图,这种对象之间相互关系的研究,为好的建模提供了重要的基础。
下面,我们再为“订购配置了的电源设备”这个用例,创造序列图,这个序列图表达了跨越几个用例的行为。
描述:
在开始两个消息的作用,我们在上面的序列图已经说明过了。
消息acceptConf产生发送给:Order对象的prepareForOrder消息,这就创建了一个Order对象,它在:OrderWindow中显示。
针对客户对预定细节的接受情况(submitOrder),:OrderWindow触发(storeOrede)一个永久的:Order对象的创建,然后,这个:Order对象把它自己链接到所预定的:ES和相关的:Customer和:Payment对象上,一旦这个对象被永久地链接起来,这个:Order对象就发送emailOrder消息给外部参与者:Customer。
注意,:Customer机作为外部参与者对象又作为内部类对象的双重用法,这在建模中是经常出现的矛盾,客户对系统来说既是外部的又是内部的,作为外部的,它与系统交互,作为内部的,客户信息又必须保持在系统里面,以识别一个外部客户是否是系统已经知道的一个合法内部实体。
参照前面的活动图,可以画出序列图。这个序列图分成两部分,Order和OrderWindow的生命线在这两部分中被重复使用。
这里只显示消息的激活,消息的返回是隐式的,同时也不需要指出参数。
五、用操作契约
(Contract
)
增加用例细节
操作契约可以帮助定义系统行为,它是用概念对象的状态改变,来描述系统操作执行的结果。有的时候更详细的系统行为描述也是有价值的。
在执行一个系统操作以后,契约根据概念模型中对象的状态变化来描述详细的系统行为。
契约是为系统操作(system operation)而制定的,这种操作,是作为“黑箱”的系统,再它的公共接口中提供的。
贯穿所有用例的全体系统操作,定义了公共的接口。
而系统,作为一个整体,可以用一个类来表示。
我们看下面的例子:
契约2
:enterItem
|
操作
|
enterItem(iyemID:itemID,quantity:integer)
|
交叉引用
|
用例:Process Sale
|
前置条件
|
有一个销售正在进行
|
后置条件
|
1
,创建一个SalesLineItem实例sli(创建实例)
2
,sli和当前的Sale形成关联(关联形成)
3
,sli.quantity变成quantity(属性修改)
4
,在itemID匹配的基础上,sli和ProductSpeciyfication
形成关联(关联形成)
|
1
、契约条目的描述
契约名:操作名
|
操作
|
操作以及参数的名称
|
交叉引用
|
[
可选]可能发生这个操作的用例
|
前置条件
|
在操作执行以前,概念模型中系统或者对象状态的值得注意的假
设,它们在此操作的逻辑内不会得到测试,而被假设为真。另外,
他们并非无关紧要,而是要让阅读者了解有这个假设存在。
|
后置条件
|
操作完成以后,概念模型的状态,后面就会详细讨论。
|
2
、后置条件
注意,在前面的例子中,每个后置条件都包含一个分类,比如“实例创建”(instance creation)或者“关联形成”(association formed),这是一个关键点。
后置条件描述概念模型中对象状态的变化,概念模型对象状态的变化包括:实例创建或删除、关联形成或断开、属性改变。
后置条件不是系统操作中要执行的动作,而是系统操作完成以后,为“真”的关于概念模型的声明。
关于关联断开:可以这样描述,“选定的SaleLineItem和Sale之间的关联就断开了”,还有“当偿还了贷款后,关联就断开了”等。
实例删除非常少见,这是人们一般不太关心强制的销毁一个事务。
后置条件一个特点是,它是声明性的,它描述的是状态变化,而不是一个动作的执行过程。
1
)分析细节
契约以声明状态变化的方式来表达问题,使它成为一个优秀的需求分析工具,在不需要说明操作如何实现的情况下,描述系统操作如何引起系统状态的变化。后置条件可以分析细粒度的信息,并指明系统的状态是什么。
这样就可以很好的分析我们所关心的一些细节问题。
2
)后置条件应该达到怎么样的详细程度
人们往往关心后置条件要书写到怎样的详细程度。
事实上,并不一定需要契约。
但假设需要一些契约,但产生完整、详细的后置条件是不可能也是不必要的。
这就是系统架构师的能力问题,如何抓住重点。
不过,在设计工作阶段,我们会发现一些细微的细节,这不见得是坏事,这些发现可以通知后面的迭代需求工作,这正是迭代开发的优点,一个近期的迭代,可以丰富后一个迭代的调研和分析工作。
经常在创建契约的时候发现,需要记录概念模型中出现的新的概念类、属性或者关联,我们不要受限于先前已经定义的概念模型,在思考操作契约并且有新的发现的时候,可以进一步丰富概念模型。
3
、书写契约的指导原则
要创建契约的时候,必须:
1) SSD识别系统操作
2)对于那些复杂的,结果微妙的以及在用例中不清晰的系统操作,可以构造一个契约。
3)要描述后置条件:
l 实例的创建和删除
l 属性修改
l 关联形成和断开
一些建议:
l 后置条件的陈述应该采用过去时态的声明语气(was…),以强调系统状态的变化,而不是强调这种变化是如何设计实现的,比如:
(较好)A SalesLineItem was created
(较差)Create a SalesLineItem
l 不要忘记在新创建的对象和已经存在的对象之间保持记忆关系。这个记忆关系就是两个对象之间的关联关系。例如,当enterItem操作发生的时候,会创建一个新的SalesLineItem实例,这还不够,当操作完成之后,在新创建的实例和Sale之间,还应该建立一个关联关系,即:
SalesLineItem和当前的Sales建立关联(关联形成)
注意:
最常见的错误,就是遗漏了关联的形式,特别是在创建了新的实例以后,往往需要和多个对象之间建立关联,千万别忘了!
六、用协作图分析复杂操作
虽然用顺序图可以很好的表达操作和行为,而且时间关系也很清楚,但是,当关系非常复杂的时候,往往图就很复杂,这样反而不利于看清问题,所以,某些情况下使用协作图将会使表达更清楚。
协作图只对相互间具有交互作用的对象和对象间的关联建模,而忽略了其他对象和关联。注意,我们并不需要对系统所有的部分绘制协作图,而只是对最主要最复杂的部分做这样的讨论。
1
、消息
消息可以用依附于链接的带标记的箭头表示。每个消息包括一个顺序号、一张可选的前任消息的表、一个可选的监护条件、一个名字和参量表、可选的返回值表。
2
、流
通常,在完整的操作中协作图包含对象的符号。然而,有时对象具有不同的状态并且必须明确表达出来。
例如,一个对象可以改变位置,或者在不同的时刻它的关联有很大区别。对象可以用它的类与它所处的状态表示,这就是具有状态类的对象。同一个对象可以表示多次,每次有不同的位置和状态。
代表同一对象的不同对象符号可以用变成流联系起来。
变成流是从一个对象状态到另一个的转换。它用带有构造型《 become 》的箭头表示,并且可以用顺序号标记表示它何时出现(如下图所示)。
协作图和顺序图都表示出了对象间的交互作用,但是它们侧重点不同。顺序图清楚地表示了交互作用中的时间顺序,但没有明确表示对象间的关系。协作图清楚地表示了对象间的关系,但时间顺序必须从顺序号获得。顺序图常常用于表示方案,而协作图用于过程的详细设计。
下图为一个协作图。
可以将对象标识成四个组:
存在于整个交互作用中的对象;
交互作用中创建的对象(使用约束 {new} );
在交互作用中销毁的对象(使用约束 {destroyed} );
在交互作用中创建并销毁的对象(使用约束 {transient} )。
设计时可以首先表示操作开始时可得的对象和连接,然后决定控制如何流向图中正确的对象去实现操作。
虽然协作直接表现了操作的实现,它们也可以表示整个类的实现。在这种使用中,它表示了用来实现类的所有操作的语境。这这使得对象在不同的操作中可以担当多种角色。这种视图可以通过描述对象所有操作的协作的联合来构造。
七、状态图及其讨论
状态图可以表达一个对象的状态变迁,事实上,在分析的时候,常常并不总是需要这种状态变迁的讨论,但是,有时候在最重要的类上进行这种讨论,也是有意义的,因为这可以为我们下一步系统设计打下了基础,我们来看下面的案例。
案例:订单处理子系统
考虑Invoice对象的状态, 有两种支付方式,初始状态是未付款,形成发票有两种可能的状态变迁,可以用下面的状态图表达。
另一方面,我们可以给Order对象画出状态图,一个新订单可能有两种状态变迁,可以看出来,这对细腻的描述订单的状态变迁是很有意义的。
第七节
迭代计划和风险管理
我们现在再把注意力回到迭代开发上来,迭代计划和项目管理是一个大问题,但为了能够实用,我们必须把问题简化,讨论一些关键问题。
所有的问题都集中在:
l 在下一个迭代中该做什么?
l 如何在迭代开发中跟踪需求?
l 如何组织项目工作?
一、区分需求的等级
1
)早期迭代的驱动因素:
风险、覆盖范围、重要性和技能发展。
在最早的迭代中,要根据风险、覆盖范围、重要性来组织迭代。
需求风险包括技术复杂度和其它因素,如工作方向的不确定性、拙劣的规范说明,行政问题或者可用性。
覆盖范围:
早期迭代至少要涉及系统的所有主要部分,可能还要充分或粗略的实现许多组件。
重要性:
具有很高业务价值的的功能,即使没有技术风险,在早期迭代中至少部分实现中要场景所需的主要功能。
有些工程的另一个驱动因素是技能培训,即帮组开发人员掌握新技术,在这些工程中,技能培训是高优先级的驱动因素。
2
)如何进行等级划分
UP是用例驱动的,所以具体实施是对用例进行等级划分。
另外,有些需求被描述为和特定用例无关的高优先级特性,它可能会跨越几个用例。这时需要描述在用例的补充规范中。
因此,优先级列表要包括用例和高层特征两个部分。
优先级列表
|
需求
|
类型
|
优先级
|
Process
|
用例
|
1
|
Logging
|
特征
|
2
|
……
|
……
|
……
|
3
)优先级划分的小组定位法
优先级可以是定性的,可以采取小组定位法。
可以在小组会上投票,事实上每次做迭代计划的时候都可以进行这种投票活动。
4
)优先级划分的定量法
需求和风险优先等级的划分,可能小组计点投票就已经足够了,不过这是模糊的定性方法,如果需要进行更定量的思考,需要进行以权重为基础的分类。
架构权重分析表
|
需求
|
类型
|
AS
|
风险
|
重要性
|
权重和
|
Process Sale
|
UC
|
3
|
2
|
3
|
15
|
Logging
|
Feat
|
3
|
0
|
1
|
7
|
Handle Returns
|
UC
|
1
|
0
|
0
|
2
|
……
|
……
|
……
|
……
|
|
|
说明
|
名称
权值
范围
AS:架构上的重要性
2 0 - 3
风险:技术、复杂、创新
3 0 - 3
重要性:早期较高的业务价值
1 0 - 3
|
二、划分项目风险等级
划分全部项目风险等级的一个有效的方法,就是从成本、时间或者工时上估计它的发生频率和影响力。这种评估可以是定量的(通常需要深入的思考),也可以是简化定性的(小组讨论、投票,简单的分成:高、中、低三级)。
最糟糕的风险,是那些很可能发生又影响很大的风险,例如:
风险
|
可能性
|
影响程度
|
缓解方法
|
缺乏熟练的面向对象开发人员
|
高
|
高
|
l 阅读书籍
l 雇用临时的咨询顾问
l 课堂教学或者培训辅导
l 两人一组编程
|
演示程序不满足即将产生的Hamburg的POS规范
|
中
|
高
|
l 雇佣拥有POS系统开发经验的临时顾问。
l 识别演示程序中已经实现的最严重的需求,并安排较高的优先级。
l 最大限度的利用以完成的组件。
|
三、在迭代之间跟踪需求
我们已经很清楚,迭代开发中并非所有的场景都在第一次迭代中实现,一个复杂的场景,往往要花6个月采用多次为期两周的迭代来完成。每次迭代都处理新的场景或者场景的某些部分。在这样的情况下,必然出现了一个需求跟踪的问题,人们如何记录用例的哪些部分已经完成,哪些部分正在进行中,哪些部分还没有涉及呢?
这就需要用到需求工具。
Rational的RequisitePro是一个需求跟踪工具,值得我们花时间掌握它来跟踪在迭代之间已部分完成的用例。
我们可以把RequisitePro和Microsoft word整合到一起,用word编辑需求,在RequisitePro中选择短语,并把它定义成被跟踪的需求。每个需求都有多种属性,如状态、风险等,RequisitePro工具可以使我们可以在迭代之间跟踪用例的部分完成情况。这个工具的使用很简单也很有效。
第八节
领域建模的实例分析
一、如何通过领域模型来发现出类及其关系
建模
实例一:我们使用一个猜数游戏来说明如何建立领域模型问题:输入一个数,如果猜中了显示“你猜中了啊”然后程序结束,如果猜的不对,系统则告诉你的数是太大还是太小,然后要求你重新输入新的数,直到猜中为止。
(1
)开始归纳问题-----其实是描述出用例的事件流
+-----------------------------------------------------------------+
|
系统应该准备一个正确答案 |
|
玩家可以输入一个答案 |
|
系统应该比较玩家输入的答案和正确答案 |
|
系统应该显示玩家每次输入的结果 |
+-----------------------------------------------------------------+
在归纳问题时,要注意把握下面的几个
基本要点
l
第一是不要涉及内部的流程,别出现“如果输入不正确,就怎么怎么样”的句子,这些并不是正确的问题,正确的问题必须是明确的,清晰的,如果可能的话全部按照“什么可以干什么”的格式来写。
l
第二是不要一开始就进入细节,包涵太多细节的问题将会是一个长长的清单,这种清单根本没什么用。尽量从最高一层分析,但也不要简单到“用户可以玩游戏”这种笼统的问题。
l
总之一个原则是全面、清晰、明确。要做好问题域分析完全取决于设计师的水平与能力,这就不是可以简单的看看书能达到的了。
(2
)获得名词列表-----为发现出类提供信息
把问题清单中的名词都提出来,得到一个名词列表,这就是类的来源(不过不忙,这只是初步过程)
+-----------------------+
|
系统 |
|
玩家 |
|
正确答案 |
|
答案 |
|
游戏结果 |
+-----------------------+
(3
)筛选名词-----除掉无关的名词
但要注意的是,不是名词列表中的所有的名词都能作为类的,接下来需要进行筛选。
l 玩家是参与者,应该放到用例图上去
l 系统太笼统,不能成为一个对象的名称
l 答案和正确答案容易混淆,但称为输入答案又容易被误解成一个动作,干脆叫做玩家答案
l 结果不明确,察看前面的需求,应该分解成错误信息和完成信息
(4
)根据名词列表发现出其中的类
最后,对前面的名词列表进行筛选完毕后,得到一个下面的名词列表
+---------------------+
| 正确答案 |
| 玩家答案 |
| 错误信息 |
| 完成信息 |
+-----------------------+
在这个列表中缺少了系统,显得太单薄,回过头再仔细察看需求,应该引入一个游戏引擎,由它来充当调度者(控制类)。同时,我们再引入一些Helper类以实现游戏的交互(边界类
)
+-----------------------+
|
游戏引擎 |
| 正确答案 |
| 玩家答案 |
| 错误信息 |
| 完成信息
|
|
游戏
的交互 |
+-----------------------+
从而发现出问题域中所隐藏的分析类。但要注意的是,在这个阶段中所发现出的类,并不是最终要实现的类,而是一个分析类。利用分析类的主要目的是帮助设计师理清系统的关系,将需求文稿中错综复杂的关系整理成一个脉络清晰的完整系统。根据规则,应该包含有三种分析类:边界类、实体类和控制类。在本问题中,它们分别是:
边界类:
游戏的交互
实体类:
正确答案、玩家答案、错误信息和完成信息
控制类:
游戏引擎
(5
)进一步修改前面的问题域,以获得更清晰的需求描述
同时修改前面的问题域,现在系统已经明确是一个游戏引擎。这种替换当然是一种理想情况,通常都会发生分解和关联,那时候需要扩充问题域,有时候还需要建立新的问题域。
+-----------------------------------------------------------+
|
游戏引擎应该准备一个正确答案 |
|
玩家可以输入一个答案 |
|
游戏引擎应该比较玩家答案和正确答案 |
|
游戏引擎应该显示玩家每次输入的结果 |
+-----------------------------------------------------------+
(6
)分析类的层次(纵向关联)
在前面,我们已经初步对问题域进行了分析,获得了一个类名的名词列表,接下来的工作则是要绘制这些分析类的层次
。现在我们要用我们的专业知识来归纳类了。很明显,错误信息和成功信息需要一个基类,玩家答案和正确答案也是一样。
(7)分析类之间的关联(横向关联)
通过对项目中的类层次进行分析和描述以后,接下来则是要分析类之间的关联,同样我们也应该从需求文档和问题域中寻找线索。在本问题中,“游戏引擎应该准备一个正确答案”,即“游戏引擎”与“正确答案”之间有关联。
(8
)最后,设计出本问题例的类的分析图(关系说明----
静态分析)
(9
)设计出游戏交互的顺序图----
动态分析
(10
)设计出类中的属性和方法---
进一步细化分析类以最终产生出设计的类
类的关联在类图已经完整地设计出后,应该为类分配方法和属性了,这将是以后的设计阶段的工作。
二、建立领域模型总结
(
1
)可以从用例的事件流开始,看看事件流中的名词与名词短语,找出大量重要的域对象以获得所要用到的类(抽取对应于业务实体或事件的名词),然后再分析这些类的属性、操作和它们之间的关系。
(2
)将名词进行分类、抽取出合适的类;同时将名词与名词短语成为对象与属性,而动词与动词短语成为操作与关联。
(3
)审查类及属性
l 是否在系统责任之内
l 是否描述类对象的特征
l 是否存在冗余
l 是否有复杂结构的属性
l 根据对需求的理解进行细化
(4
)所要注意的几点
l
素描类的特性
需要强调的是,在这一阶段对特定领域类的描述具有一定的素描性质。也就是说特定领域类的操作和属性不一定与最终实现时的定义一致。
因为此时还没有涉及到系统功能的具体实现,不可能准确、完整地定义它们。有一些操作需要在设计阶段细化时才能确定。
l
动态行为的描述
此外,为了描述领域类的动态行为,可以使用UML中的任何一种动态图(如顺序图、活动图、合作图、状态图)来描述。
三、建模实例二:
某一网站领域模型的建立例
(1
)用户所罗列出的一些需求
l 我需要做一个网站
l 我的文章、我最近的活动(新闻)、下载和留言
l 我希望把我平时的文章发布出来给用户看
l 我希望把我最近做的讲座放到我的活动栏目中来进行宣传
l 我还会把我的一些源代码和讲座录像打包发出去
l 我希望用户给我留言
(2
)需求分析
l
功能性需求
ü 网站可以提供我平时写的文章的链接
ü 网站可以将我最近的活动情况发布出来
ü 网站能够将我平时的源代码和讲座录像打包发出去
ü 网站能够接受用户给我的留言
l
非功能性需求
网站要简单、美观和大方;浏览的速度尽可能快。
(3
)找出名词短语------
领域模型
网站 平时文章 最近活动新闻 平时源代码 讲座录像 用户留言 管理员 文章 讲座 活动 新闻栏目
源代码 讲座的录像包 浏览者
留言
|
(4
)发现类及类之间的关系
l 创建“用户留言”功能模块的包
l 在该包中分别添加各个分析类
l 设置“用户留言”功能模块中的相关的各个分析类的关系
l 给某个类设置其属性和方法
l 再设置其属性的数据类型和访问限制
l 给该类添加方法
l
设置“用户留言”功能模块中的交互的顺序图----
动态分析
名称为“用户留言模块的顺序图”
分别从用例图中拖动参与者“用户”和从“留言功能包”中拖动“留言表单”、“用户留言”和“留言”、“留言成功”等类到视图中。
然后再分别设计其消息的发送和返回的次序。
注意:在上面的顺序图中所出现的各个消息没有映射成对应类的操作方法,如“填写表单”消息没有映射成“留言表单”类的操作方法。因此在后面进行
用Rose生成代码中进行“检查模型”时会出现错误。
l 根据顺序图创建出协作图
四、建模实例三
下面给出“铁路呼叫中心”项目的功能性和非功能性的需求,从而获得“问题域”中的相关的类;
(1)呼叫中心项目的功能性需求
旅客应该能够通过系统进行车次信息的相关查询
旅客应该可以通过系统进行相关车次余票的查询
旅客应该能够通过系统进行订票
旅客应该能够通过系统提供投诉意见
旅客应该可以通过系统进行行包的办理
系统应该可以取消订票
系统应该可以记录旅客的操作步骤
系统语音提示应该尽量的简单清楚
系统应该可以记录对方用户的电话号码等信息
系统应该具有少量的人工坐席和大量的自动坐席
系统应该能够及时的响应客户订票,并通知订票结果
系统应该可以自动地给旅客分配一个订票号和相应的订票密码
系统应该可以按照车次查询时刻表
系统应该可以按车次查询到站时刻
系统应该可以根据始发站和到达站或途径站的车次信息
系统应该可以为团体客户提供团队订票业务
系统应该可以为旅客提供原有订单的查询
(
2
)
呼叫中心项目的非功能性的需求
订票流程应该尽量的简单
订票流程的操作时间应该尽量的短
系统应该能够控制订票操作的时间
系统应该能够取消故意骚扰系统的电话
系统应该有预留接口可以方便的连接到其他的客服服务电话号码中去
旅客 呼叫系统 车次信息 车次余票 火车票
投诉意见 行包 操作步骤 语音提示 用户信息 人工坐席 自动坐席 订票结果 订票号 订票密码 时刻表 到站时刻 始发站 到达站 途径站 团体客户 订票业务 订单
|
(3)
找出名词短语------
领域模型
(
4
)发现出类及类之间的关系
五、建模实例四
下面给出“网上订票”需求项目的功能性和非功能性的需求,从而获得“问题域”中的相关的类;
(1)网上订票项目的功能性需求
系统应该可以允许用户进行注册
系统应该可以允许进行登陆
系统应该可以允许用户修改自己的个人信息
系统应该可以允许进行网络在线订票
系统应该可以允许可以根据车次查询列车时刻表
系统应该可以根据始发站和到达站或途径站的车次信息
系统应该可以查询票价
系统应该可以查询既有订票信息
系统应该可以取消订票
系统应该可以查询晚点信息
系统应该可以提供团体订票业务
系统应该可以允许在线支付票款
系统应该可以查询余票信息
(2)网上订票项目的非功能性需求
网络响应速度应该尽量快(订票流程的操作时间应该尽量的短)
用户填写的信息应该尽量的少,采用菜单选择和勾选方式(订票流程应该尽量的简单)
系统应该有预留接口可以方便地连接到其他的客服服务电话号码中去
(3)
找出名词短语------
领领域模型
系统 用户 个人信息 火车票 车次 时刻表 始发站 到达站 途径站 票价 既有订票信息
晚点信息 团体订票业务 票款 余票信息
|
(4
)发现出类及类之间的关系
六、建模实例五
下面给出“ATM系统自动售票系统”需求项目的功能性和非功能性的需求,从而获得“问题域”中的相关的类;
(
1
)
ATM
系统自动售票系统
的功能性需求
ATM系统应该可以接受纸币
ATM系统可以接受信用卡
ATM系统可以检验纸币的金额
ATM应该可以验证信用卡的密码
ATM系统应该可以进行伪钞的识别
ATM系统必须可以卖票
ATM系统可以验证购票的金额与信用卡的金额的比较
ATM系统可以找零钱
ATM系统应该可以订票?
ATM可以打印交易的收据凭票
ATM系统可以吐票
ATM系统可以查阅车次信息
ATM系统可以查询车票的席位余额
ATM系统应该可以对帐
ATM系统应该可以查询钱箱中的余额
ATM系统应该可以取消购票
ATM系统应该可以记录操作步骤
ATM系统应该用触摸屏
(
2
)
ATM
系统自动售票系统
的非功能性需求
ATM系统应该可以语音提示
ATM系统应该可以对于什么什么进行报警?
ATM系统购票过程应该操作步骤简单
ATM系统必须有监视功能
ATM系统应该适合不通身高的人购票
(3)
找出名词短语------
领域模型
ATM系统 纸币 信用卡 纸币的金额 信用卡的密码 伪钞 票 购票的金额 零钱 打印凭条 车次 帐 钱箱的余额 操作步骤 触摸屏
|
(4
)发现类及类之间的关系
ATM系统的取钱使用案例的Class类图
张三取款的顺序图
张三取100元钱的协作图
张三取款的状态图