统一建模语言UML基础专题之UML简介及用例图 统一建模语言uml
为什么要学习统一建模语言UML?
面向对象编程方法学是面向对象编程的指导思想。进行面向对象编程编程的第一步是利用对象建模技术(Object Modeling Technique,OMT)来分析目标问题,抽象出相关对象的共性,对它们进行分类,并分析各类之间的关系,再用类来描述同一类对象,归纳出类之间的关系。Coad和Yourdon在对象建模技术、面向对象编程和知识库系统的基础之上设计了一整套面向对象的方法,分为面向对象分析(Object-Oriented Analysis,OOA)和面向对象设计(Object-Oriented Design,OOD)。对象建模技术、面向对象分析和面向对象设计共同构成了系统设计的过程,如图所示。这是编写代码的基础。
在进行对象建模、面向对象分析和设计的过程中,需要使用建模语言来描述分析的过程和结果。统一建模语言(Unified Modeling Language,UML)是为了这个目标而设计的一种标准通用的设计语言。任何想要真正理解面向对象编程思想的人,都需要具备一定的UML知识。
在这个专题中我们会陆续介绍统一建模语言UML中的用例图(use case diagram)、类图(class diagram)、对象图(object diagram)、序列图(sequence diagram)、状态图(statechart diagram)、活动图(activity diagram)、组件图(component diagram)和部署图(deployment diagram)。
统一建模语言UML简介
UML不是一种方法学,而是一种描述语言,它提供了多种类型的模型描述图(diagram),当在某种给定的方法学中使用这些图时,人们就能更容易理解和交流设计思想。UML的图可划分为如下三种类型。
— 静态图(static diagram):描述了那些不发生变化的软件元素的逻辑结构,描绘了类、对象、数据结构及其存在于它们之间的关系。
— 动态图(dynamic diagram):展示了在运行期间的软件实体的变化,描绘了执行流程、实体改变状态的方式。
— 物理图(physical diagram):显示了软件实体的不变化的物理结构,描绘库文件、字节文件、数据文件等,以及存在于它们之间的关系。
下表说明了三类图中最常用的一些UML图,以及它们适用的软件开发阶段。
提示 Microsoft Visio可以用来方便地绘制UML图。Visual Studio 2005的类关系图也可以显示类似UML的图,但是与标准的UML稍有不同。
统一建模语言UML用例图
用例图描述了系统提供的一个功能单元。用例图的主要目的是帮助开发团队以一种可视化的方式来理解系统的功能需求,包括基于基本流程的“角色”之间的关系,以及系统内用例之间的关系。用例图一般用于表示用例的组织关系,要么是整个系统的全部用例,要么是完成具有功能(例如,所有安全管理相关的用例)的一组用例。
用例(use case)指的是系统的功能,它是系统某个功能的所有执行动作的集合。用例是从一个用户的观点来描述的。这个用户告诉系统去做一些特定的事情。一个用例捕获一个事件的可视化序列,这个事件是一个系统对单个用户的激励(stimulus)的响应过程。
动作者(actor)表示系统用户能扮演的角色(role) 。这些用户可能是人,也可能是其他的计算机、一些硬件,或者甚至是其他的软件系统。对用例的唯一要求是,它们必须位于用例描述的系统部分之外,它们必须能刺激系统部分,并接收返回。
用例描述了当动作者之一给系统特定的刺激时的系统活动。这些活动用文本来描述。它描述了触发用例的刺激的本质,输入和输出到其他活动者,以及从输入到输出的活动。用例文本通常也描述每一个活动可能的错误和系统应采取的补救措施。
在用例图中,用椭圆来表示用例,并将用例的名称放在椭圆的中心或椭圆下面的中间位置。人形符号用来表示角色(用户)。角色和用例之间的关系使用简单的线段来描述,表示角色可以操作此用例。
在用例图中,用一个方框来表示系统的边界。所有系统用例都放在框内,所有动作者都位于框外。动作者和用例之间用直线相连。方框内的每一件事物都是系统的一部分,方框外的每一件事物都是系统的外部。用例图也可以表示方框内的系统用例之间的关系,最常见的是“使用关系”,用带箭头的直线来表示,箭头指向被使用的用例。还有一种关系是扩展关系,用来表示继承。
图6-2显示了一个用例图。用例图通常用于表达系统或者系统范畴的高级功能。在图中可以很容易地看出该系统所提供的功能。这个系统允许乐队经理查看乐队CD的销售统计报告及排行榜报告。它也允许唱片经理查看特定CD的销售统计报告和这些CD在排行榜的报告。这个图还告诉我们,系统将通过一个名为“排行榜报告服务”的外部系统来提供排行榜报告。
用例图不会列出系统不能完成的功能。下图所示的系统不能提供给乐队经理收听排行榜上不同专辑中的歌曲的方法,也就是说,系统没有引用一个叫做“收听排行榜上的歌曲”的用例。如果在用例图中提供清楚的、简要的用例描述,项目赞助商就能很容易地看出系统是否提供了必需的功能。
posted @ 2010-03-04 14:05 Silver.Lee 阅读(193) 评论(0) 编辑
如何写好最外围用例
2003 年,我曾经在网上看到一篇技术文章《 没头没尾—项目开发笔记:如何编写最外层用例 ?! 》,这篇文章写得很好,真实记述、反映了用例写作中的一些常见现象和误区。 然而,该文的前后两个用例实际上都不是针对客户的真正的“最外层用例”,尽管作者作了些改进,但这些用例在具体的内容和写法上还存在一些问题。
1.引言
“本系统的目标是制作出一个跨企业的信息平台,为目标公司的客户进行服务。这些服务分成很多的方面,比如提供给银行的公司财务情况查询,提供给生产厂商销售情况的报告。提供给销售的商场以及店铺货源的情况,提供给物流,安装服务公司送货以及安排信息。从中赚取提高销售效率,减少运输损耗的费用。设定目标公司为 A公司,信息平台名称B系统。”
从上面介绍的内容来看,我猜 B 系统是 一个 类似于供应链 应用 的 企业 门户 系统,这里所说的 A 公司好像是一家从事商品分销的企业。通过该门户, A 公司可以有效地联系上家—生产商(供货商),下家—商场、店铺以及银行、物流公司、安装服务公司等客户或合作伙伴。
在讨论这个案例前,首先让我们重温一下什么是最外围用例( the outermost use cases ,根据 Cockburn 的定义改写) :
对于每一个用例找到它仍能适用的最外圈的设计范围,针对该范围为它编写一个概要用例; 对于一个将要设计的系统, 我们 通常可以找到一个更广的设计范围,而使主用角( PA , Primary Actor )仍然处于该范围之外,如果不断扩大该范围,可以找到一个临界点,主用角就会被包含在当前的范围之内,这个临界点就是最外圈的范围; 最外围用例是通过把同一个范围内具有相似目标的主用角合并到一起而创建出来的,它们把与这些用角有关的所有低层用例都汇聚到了一起。
如何获取最外围用例呢? Cockburn 给了一些步骤建议 :
(1) 从一个用户目标开始。
(2) 问“这个目标对主用角 AA (最好在组织外部)提供什么服务?”,用角 AA 是我们想要收集用例的最终主用角。
(3) 找出最外圈的设计范围 S ,使得 AA 仍然在 S 之外,给 S 命名。
(4) 找出最终主用角 AA 在设计范围 S 中的所有用户目标。
(5) 找出 AA 对系统 S 的概要目标 GG 。
(6) 为 GG 编写出概要用例。这个用例就是我们要找的最外围用例,它将一些海平面(用户目标层)的用例维系在一起。
值得注意的是,这里 Cockburn 所说的“系统” S 不一定都是指软件,它可能是软件系统(如 B 系统),也可能是业务系统(如 A 公司、企业、部门等)。 在下面第 2 、第 3 小节中我将按照 Cockburn 的最外围用例 定义 来考察 原文中的两个最外围用例 ,逐个分析存在的错误。用第 1 个例子说明以“操作人员”为 主用角 、最外圈范围为软件系统( B 系统)的正确写法,用第 2 个例子说明以“客户”为主用角 、最外圈范围为业务系统( A 公司)的正确写法。
2.软件用例 - 业务管理
用例名称: 最外围用例
错误分析: 显然这不是一个正确的用例名称。建议改为“业务管理”,解释如下文。
简要说明:
A 公司决定让其业务合作伙伴以及最终的用户通过互联网访问 B 系统以达到减少 A 公司本地人员的工作量以及提升工作效率的目的。 B 系统负责完成进销存业务、其它业务处理以及报表管理。一些系统维护人员还负责管理基础信息的认定与实现。包括四个用例:基础设置子系统,进销存子系统,其它业务处理子系统,报表子系统。
错误分析: 首先,用例的名称、内容中不应该出现“子系统”这个术语, subsystem 属于系统设计范畴。
从业务介绍来看,操作人员主要进行“进销存、报表和其他业务”的处理,系统维护人员主要进行基础信息管理,所以这 4 个概要用例(风筝层)分属不同的 PA 。这两种角色在概要白云层似乎没有必要严格区分,所以我们把系统维护人员和操作人员合并成“操作维护人员”,并且把这 4 个用例合并成 1 个最外围用例“业务管理”,并在此处描述。
用例范围: 跨企业信息平台
主执行者( PA ): 客户
错误分析: 从用例范围和基本流的内容看, 这里的 PA 应该是“操作维护人员”(客户作为 PA 的最外围用例见第 3 小节)。
用例层次: 白云(最高层次)
触发事件: 客户有购货的需求
错误分析: 这里应该描述可以检测到的事件(动作、状态的改变等),我们怎么知道客户有购货的需求呢?正确的说法可能是:客户以各种方式请求服务。
主成功场景(基本流)
· 系统维护人员操作基础设置子系统维护基础设置的数据。
· 操作人员操作进销存子系统完成进销存业务。
· 操作人员通过报表系统分析查询业务结果。
· 操作人员通过其它的业务子系统完成对系统中的其它业务处理。(注,其它业务子系统包括财务资金等等子系统,将会子用例描述中强调)
小结:
从以上分析看,原用例存在着基本的概念错误,用例的主用角( PA) 、范围和基本流的内容相互之间存在不一致的现象。这 个最外围用例可以改写如下:
用例名称:
业务管理
范围:
B 系统
层次:
概要 / 白云
主用角:
系统操作维护人员
触发事件:
客户通过电话、邮件等方式向操作维护人员提出服务请求(如购货)。
基本流:
操作维护人员通过 B 系统可完成以下任务:
1. 基础信息管理。
2. 处理进销存业务。
3. 进行报表管理,查询、分析业务结果。
4. 处理其他业务(如财务资金管理)。
[ 用例结束 ]
3.业务用例 - 客户服务
用例名称: 最外围用例
错误分析: 显然这不是一个正确的用例名称,建议改为“客户服务”。
简要说明:
A 公司决定让其业务合作伙伴以及最终的用户通过互联网访问 B 系统以达到减少 A 公司本地人员的工作量以及提升工作效率。 B 系统负责完成销售业务以及报告销售情况。一些系统维护人员还必须为客户和职员设置安全存取级别。包括四个用例:增加服务( A 公司本地),增加服务(客户),报告业务情况,管理安全存取权限。
错误分析: 这段话其实不太像用例简述。最外围用例应该从 A 公司向客户提供服务的角度来叙述,比方说,可以请求服务,查询业务销售报表等。在本例中我们进行黑盒业务建模,涉及到 B 系统和系统维护人员的内容,比如安全级别设置,不应在此处反映。
用例范围: 跨企业平台
错误分析: 我们选定了 A 公司的客户作为 PA ,那么按照最外围用例的定义,最大范围边界应为 A 公司而不是 B 系统。实际上这是一个业务用例( Business Use Case )。虽然客户也可以直接访问 B 系统,但是对于客户而言,B 系统 不是最大的范围 。
用例层次: 白云(最高层次)
主执行者( PA ): 客户
主成功场景(基本流):
·客户通过电话,邮件联系 A 公司操作人员,请求一个新的服务。 A 公司的操作人员通过 B 系统处理服务请求。
·客户直接通过 B 系统的客户端,请求与处理新的服务。
·客户通过 B 的系统可以查询出销售情况以及其它相关情况的报表。
·B 的系统维护人员将要对客户以及操作人员进行安全存取权限的设置。
错误分析: 在最外围业务用例的基本流中,主要包含一些用户目标层或概要层的用例,所以此处不应该出现与 B 系统、操作人员、系统维护人员相关的内容,它们在我们定义的范围边界( A 公司)之内。 根据以上分析,我将该 最外围用例改写如下:
用例名称:
客户服务
范围:
A 公司 / 业务
层次:
概要 / 白云
主用角 :
客户
触发事件:
客户提出服务请求
基本流:
客户可以下列 2 种方式获得服务:
1. 服务请求处理:客户通过电话、邮件、客户端软件等方式请求服务, A 公司接收请求后进行相应处理,并把处理结果以恰当的方式返回给客户。
2. 销售查询:客户通过客户端软件直接查询销售情况及相关报表。
[ 用例结束 ]
4.总结
有人认为:“ 也许针对一个项目可以有很多‘正确的'最外层用例的设计方法。上面两种划分方式应该说只从最外层用例的角度来说都是正确的 ” 。我不同意这种开脱的说法,针对 1 个项目抽取最外围用例实际上只有 1 种“最优解”。为什么?道理很简单,因为最外围用例是 Cockburn 提出的,他给出了找到最外围用例的步骤和方法,而这种方法是明确的、无二义性的。人们找错了最外围用例,多半是因为没有理解和掌握这套方法。
概括一下,原文主要存在以下错误:
1 )由于用例的主用角 、范围与基本流的内容不一致,导致那两个用例都不是真正的最外围用例。实际上,针对“客户”的最外圈范围是 A 公司,而针对“操作维护人员”的最外圈范围是 B 系统。
2 )在用例的功能描述中出现了软件内部设计的内容,不符合需求提取、分析的要求。
最终,由于原作者对于为什么要编写用例,用例与传统结构化方法的功能总体描述究竟有何实质上的不同,用例有哪些特点,缺乏准确而深入的理解,导致编写用例的时候思路凌乱,把几种内容混杂在一起,做成了一碗“杂烩面”。 这个案例给我们的重要启迪是,抽取用例最关键的一步是首先明确 SuD ( System Under Discussion or Design )范围的定义 以 及针对这个范围的主用角 。 如果用 传统 “结构化”的老思路来套用例的格式,换汤不换药,那对 IT/软件项目开发将起不到真正的效果,甚至可能产生负作用,如果那样还不如不写用例。 最外围用例便于我们将关注的焦点转向用户真正需要什么,从而真正地从用户的角度出发来考虑问题
posted @ 2010-03-04 14:04 Silver.Lee 阅读(70) 评论(0) 编辑
统一用例分析
一、用例基础
1.1用例简史
用例技术大体上经历了萌芽、成熟和发展3个阶段[12],最早可追溯到上世纪60年代末UML(统一建模语言)、RUP(Rational统一过程)之父Ivar Jacobson(伊瓦·亚克申)博士在著名的瑞典爱立信公司领导程控电话交换机开发时采用的traffic case(话务案例),1986年前后亚克申博士在OOPSLA大会上发表的论文[4]标志着用例的正式诞生。1992年,亚克申博士在其名著《面向对象软件工程:用例驱动方法》[3]中正式推出了当时已相当完善的用例方法,用例驱动成为了Objectory过程(RUP前身之一)的核心内容,从此用例在国外软件工程界得以迅速普及,并于90年代中后期被RUP和UML吸收为核心要素,亚克申博士在《统一软件开发过程之路》[4]和《统一软件开发过程》[5]中对此作了精彩的阐述,这些书因为成为了解其用例思想的重要著作。如今,作为必不可少的关键内容,用例技术总是被所有的当代需求工程名著所引用,而“用例”本身也几乎成为功能需求的代名词。(注:考虑到历史上的渊源关系,为了方便起见,以下我们用术语“亚克申方法”或“亚克申用例”来统称RUP、UML及其支持者所采用的用例方法,尽管它们彼此之间可能存在细微的差别,而且也不一定全部是由亚克申博士本人所提出或赞成的。)
用例另一支主流派别代表人物Alistair Cockburn(阿里斯代·寇本)上世纪90代初从亚克申那里学习了用例,随后通过十年认真广泛的实践对其进行了继承和发展。寇本于1995至1997年间提出了著名的“基于目标的用例”方法[2],他的方法和思想集中体现在《Writing Effective Use Cases》(以下简称为WEUC)[1]中,以结构化/半结构化文本用例为中心是寇本方法的一大特色,该书可以说是迄今为止最为详细的一本用例教材,对于指导实践者如何写好文本用例具有很高的价值。
1.2用例定义
下面,先让我们从什么是用例开始讨论。亚克申定义[7]强调用例是系统执行的一个动作序列(注:这其中也包括与用户的交互),这些动作必须对某个特定的使用者(Actor)产生可观测的、有价值的结果。(注:Actor实质上是用户所扮演的一种角色)
那么到底什么结果叫“可观测、有价值”呢?虽然两种用例本质上是一致的,但亚克申定义对此没有明说,而寇本定义[1]则更加完善和明确。它首先强调用例是各种系统受益人(Stakeholder,又译“干系人”)之间的一种行为契约(注:行为包括对象的活动、动作和对象之间的交互等),建立契约的目的是为了达成某种目标,因此每一个用例及其名称实际上都应代表一个用户目标,这个目标是否得到真正满足正是判断我们抽取的某个用例是否“有价值”的关键。寇本还点出了要通过用例的具体执行来展现Actor的目标是如何实现或失败的,而一个用例其实就是多个在不同条件下执行并可能导致许多不同后续状态的情节(scenario,又译“场景”)的叠加,这就是用例结果的“可观测”。
因此综合起来,我们只要抓住这样几个关键词:目标、行为契约、行为(事件)序列(动作和交互)、情节、可观测、有价值,就可以比较准确地描述出用例的本质特征。
1.3用例的重要性
为什么用例如此重要?一言以蔽之,这是因为用例是一种普遍存在的客观现实,而实践证明,用例技术是迄今为止最为深刻、准确和有效的系统功能需求描述方法。
功能需求是指系统输入到输出的映射以及它们的不同组合[9],任何功能必然要通过外部环境与系统之间的交互才能完成,这不正是用例所要反映的内容吗?因此,我们可以在内容和形式上把用例和系统的功能需求等同起来,并且得出推论:只要是软件,必然都存在用例(虽然有时候不一定非要用某种具体的用例格式来描述),其中即包含数据流,也包含控制流,既包含消息发送和数据交换(交互),也包括活动/动作的执行以及状态的变迁。这些就是用例的本质(现象背后那个真实的、抽象的“胚”),而各种格式文本、UML图形(我们至少可以用4种UML动态图来描述用例)不过是用例的外部表现形式。所以,与其说亚克申博士发明了用例,还不如说亚克申博士早在20年前就发现了用例这种客观现实,并最终发明了用例表示和用例驱动软件过程的方法。
那么,什么情况下不太适合采用用例方法?主要有两种情况:(1)用户很少或没有,接口也很少,如科学计算/仿真软件、杀病毒软件、编译程序、内存管理程序等[9];(2)功能需求非常简单,非功能需求和约束占主导地位。显然,如今绝大部分的应用软件、系统软件,尤其像电信、银行、保险、税务、制造业、企业信息化等领域的复杂系统,都是符合用例适用条件的,这从一个侧面反映出用例技术的广泛适用性。请注意,即使在上面两种情况下也并不是说这些软件的用例就不存在了,而只是表明它们的功能需求很简明或不太重要,除用例之外可能还有更加适用的方法。
正确有效的软件需求必须是可测试、可验证的。过去我们描述功能需求可能采用了很多种方法,除严格的形式化方法外,普遍的缺点就是粒度太粗、精度不够,大多停留在受益人要求(request)和特性(feature)这一层[9]。比方说,针对某个“打印报表”功能,通常仅用一段话来描述它的静态输入和输出是不够的,还应该描述出用户打印报表的具体操作流程,它有哪些特殊条件和选项设置,以及它与其他需求的依赖关系等等。很多团队在需求尚未细化到用例这一层次时就开始匆匆编码了,结果往往导致大量需求风险乃至架构风险被隐藏到构造、移交阶段才发现,这必然造成频繁的返工和严重的资源浪费。及时准确地抓住需求契约——用例这一关键,可以帮助我们在不失实用性、灵活性的情况下,有效地避免项目后期大量非正常的需求变化,为进度愈来愈紧的项目赢得宝贵的时间,提高项目的成功率。
1.4相关译法
借此机会,顺便谈谈对use case有关术语翻译的看法。
笔者认为“用例”是目前较好的译法,这个词可能来源于大家熟知的“测试用例”。有人认为把use case翻译成“用例”是错误的[11],理由是:“‘例’是被列举出来以说明某种情况的个别事物,use case是对一项系统功能使用情况的普遍适应的描述,而不是对个别actor或者在个别条件下使用这项功能才适应,它也不是通过举例的方式来描述的”,所以不能叫作“用例”。此种说法不尽全面,而且有些牵强(先不管它正确与否),其实use case到底是个别的,还是群体的(普遍适应),取决于我们的视点。虽然对于单个的scenario来说,use case是多个情节的叠加,是一个整体的复合概念,但是我们知道,一个系统的功能必定是可数的、有限的,而每一个功能都可以表示为一个use case,所以在观察系统提供的所有功能需求的集合这个层面上,use case又是一个一个可数的个体(“椭圆”),每一个都代表了不同的用户目标,适用于个别的actor和个别特定的前置条件。同一个事物既是个体的又是整体的,这种现象并不足怪,例如在UML对象-类-类元关系中,通常对象是类的实例,而类又是类元的实例,对类元来说,类、接口、子系统、use case等等就是一个个个体的概念,类既是其对象实例的集合又是其类元集合的个别元素。可见,把use case的“case”译成“例”并没有错。
有的地方把use case翻译成“用况”,即“使用的情况”之意,意思的确不错(use case的另一种说法是“使用的方式”)!可我总感觉这个词比较突兀、拗口,类似的还有“用案”,把scenario叫作“案况”,大概这些词读起来不太符合大家的习惯(类似地,既然可以叫“用况”,为什么不能叫“用情”呢?),所以现在“用例”的叫法还是越来越多了。
其实“用例”这个译法还有个附带的好处,通过它我们很容易把原本就存在紧密联系的use case和test case(test case来自于对scenario的分析,而scenario是用例的一次执行)从中文名称上也方便地统一起来。不过,这里我们需要做一个小小的改进。中文的“测试用例”到底是指test case(带定语的名词词组)呢,还是指对用例进行测试(testing the use cases,动宾词组)呢?显然这两者不易分辨,而且若“用例”和“测试用例”两个词同时出现在一啰个句子或一段话中,常常会让人感觉嗦和便扭。为了消除歧义,干脆以后把test case都叫做“测例”,这样不但比以前的叫法更加简洁明了,而且无论字面上还是语义上都很贴切。当然,用例和测例是不同层面的“例”。
现在市面上Actor也有多种译法,常见的包括“参与者、执行者、主角”等等。“参与者、执行者”的问题主要是不准确。首先,“参与者” 通常让大家马上想到的词是participant,而且请注意,一个用例的真正参与者决不是只有外部的Actor,它们必然还包括系统本身及其内部的各种元素。“执行者”的问题与此类似:一个用例的真正执行者应该是系统本身!因此严格地讲这样译是错误的,兴许叫作“外部参与者”、“外部执行者”才更为恰当。“主角”的译法同样存在着矛盾。如果把Actor叫作“主角”,那么Primary Actor就应该叫作“主主角”了。看来Actor的译法中是不能含有“主”的,那么就剩下“角”了,而UML已经有了一个专门术语role(角色),我们又不能把Actor直接叫作“角色”。
目前看来,把Actor意译成“使用者”是比较妥当的。在大多数情况下Actor的的确确就是用户(确切地说是系统用户所扮演的一种角色),所以我们可以用“使用者”这个词从字面上与“用户”(user)进行区分,但同时又保持两者语义上的联系。我们还可以把为系统服务的Supporting/Secondary Actor(见下文)叫做“被使用者”(为了简化可以省略“被”字)或“辅使用者”。除了指系统的用户之外,“使用者”还有另一层含义,即Actor是use case的使用者(或被使用者),这种关系在UML用例图上应该可视化地表示为它们之间的连线(关联)。这样解释不但说的通,而且更便于不熟悉软件技术的业务人员理解。
当然,我们也不排除将来会找到“use case”、“actor”等术语更好的译法。
二、统一用例方法
2.1 理由
为什么要提出统一用例方法(UUCM),有这个必要吗?
我们发现,虽然寇本用例起源于亚克申用例,但两种用例方法各自经过十多年的发展,彼此之间逐渐出现了一些显著的差异,而且由于商业或其他方面的原因,目前我们尚未看到两者将要融合的明显趋势。两种方法各有优缺点,寇本强调基于目标的文本格式,亚克申用例则更突出UML的作用,如下文所示,两者的差别至少有10处之多。对于实践者来说,如何处理好这些明显差异,避免使用上的误区,再者能否巧妙地做出取舍,实现熊掌与鱼兼得,这些都是非常现实的在实践中必须面对的问题。
笔者认为采用统一的用例方法,把亚克申和寇本用例两者结合起来,甚至融合其他的用例方法(据说已知的各种用例表示方法多达18种以上[2][6]),在理论和实践上均是可行和必要的,这可能是我们目前可以采取的最佳策略。但是,UUCM本身并不是一种全新的用例方法,而只是一种特定的处理方案。它是在对亚克申、寇本用例方法继承的基础上,试图消除这两种经典方法的不一致和矛盾,并探索可能的优化改进和后续发展,所以UUCM起到的作用与亚克申、寇本用例方法相比是次要的和微小的。
以下我们对两种方法的异同进行比较分析,并同时给出UUCM的建议。
2.2层次
明确提出用例的层次和范围划分,是寇本“基于目标的用例方法”的精华所在[2]。显然,每个用例存在的意义是为了完成一定的用户目标。寇本把用例划分为3个目标层次:概要层、用户目标层和子功能层,并通过引入巧妙的Why/How技术帮助分析者找到合适的目标层次,从而可以有效地把握用例的粒度(真正的用例最终应落实到用户目标层),防止用例情节的爆炸。
亚克申方法及其相关文献在介绍如何有效地控制用例的粒度方面,大体上只有这样两条基本的判定规则:(1)通过判断内容是否有价值可以防止用例过小(例如,“输入发货地址”的粒度就太小,这不是一个真正的用例,相当于寇本的子功能层用例);(2)通过判断具体内容是否可观测,可以防止用例过大(例如,“用户管理”就比较空洞,这不是一个有效的用例名称,相当于寇本的概要用例)。但如前所述,究竟什么叫“有价值”,什么叫“可观测”,如何把握好这个度,亚克申方法对此语焉不详,给RUP和UML的使用者带来了不少困惑,而寇本方法恰好出色地回答了这个问题。
UUCM:
通过寇本的分析,我们发现用例有纵向(层次)和横向(范围)之分,这些是非常有价值的概念,它们是对亚克申基础用例方法的丰富和完善,两者是不冲突的,完全可以在RUP相关的实践中加以运用。
值得注意的是,我们在实践中应该尤其关注用户目标层用例。寇本引入概要层用例的主要目的是为了包含一个或多个用户目标层用例,为系统提供全局功能视图,提出子功能层用例则是为了表达用户目标层用例的具体实现步骤。虽然有时为了简便,我们也把后两者叫做“用例”,但其实它们都不是真正的用例。
可能是为了防止滥用、误用用例的分解(这很容易引诱人倒退到结构化的功能分解),亚克申方法没有提及甚至有意回避用例的层次问题。亚克申博士在最近的一篇文章中[12]引入了用例片段的概念,这意味着我们应该把寇本方法中的子功能层用例叫做用例片段以避免名称上的混淆,看来这是今后一种比较好的处理办法。
2.3范围
关于用例范围的处理,两种方法基本上是一致的,都可分为业务用例、系统用例两种,区别在于寇本强调在格式上用图标或文字显式地表示出每个用例的范围,并且在用例层次划分的基础上提出了最外层(或最外围)用例的概念[13],这在亚克申方法中是没有的。
UUCM:
用例的本质就是对象之间的一种协作和交互,这些内容是属于需求还是属于设计,关键就看你划定的讨论边界。我们不仅可以用它来描述发生在系统边界上的功能需求(用例的常规定义),而且还可以同样的方式描述系统内部发生在子系统、构件、接口或类/对象边界上的黑盒交互,不过后者往往涉及到系统设计范畴,属于用例的特殊用法,在做项目的需求分析时一般不予考虑。
正如寇本所提到的,同样一个用例名称,比如“取款”,可能实质上代表着两个截然不同的用例:一个是ATM取款(系统用例),另一个是银行柜台取款(业务用例)。所以在一个用例中明确标记出它的范围,是很有必要的。我们既可以用特定的图标,也可以用专门的格式字段来表明用例的范围,还可以对用例的名称加以修饰以便区分,比如“柜台取款”,“ATM取款”。
当前的讨论边界(SuD,the System under Discussion)一般比较容易确定,那么如何从用例的范围上判断一个用例是系统用例,还是业务用例呢?(这是一个出现率很高的FAQ)。有个小窍门:如果某个SuD或者用例的范围包含了人以及由人组成的团队、部门、组织的活动,那么针对这个SuD写出的用例必然是业务用例;如果该SuD仅仅是一些软件、硬件、机电设备或由它们组成的系统,并不涉及到人的业务活动,那么根据这个SuD写出来的肯定是系统用例。
由于系统(注意,这里的“系统”是指由软、硬件组成的IT系统)往往是业务的一部分,我们还可以得出推论:对于某个系统用例suc,通常总是可以找到一个业务用例buc,buc的范围要大于或等于suc的范围;当这两个用例的范围相等时,buc就是suc,或者buc的层次要高于suc的层次。
2.4 包含与扩展
由于各方面复杂的原因,如何更好地表示用例之间的关系一直是个争议不断的问题,而且恐怕在将来较长的一段时间内对此还难有定论。我们发现,寇本与亚克申方法在用例的扩展、包含、继承等关系的使用细节上存在着一些明显差异,其中很大一部分原因可能是由于现有文献对用例关系的定义不够精准和完善,导致大家在应用时各取所需、各自解释造成的。如果我们在实践中对这一情况不加以留意,就可能给需求的沟通和交流带来障碍。
2.4.1一头雾水
同样一对用例之间的关系,在寇本用例模型中是包含关系,在亚克申模型中却可能被表示成扩展关系。例如:
图1、寇本画法[1]
图2、《UML参考手册》画法[10]
UUCM:
为什么会有如此差别?为了说明问题,还是让我们先看看这几个用例的文本描述。《UML参考手册》写道:
基用例ATM会话:
显示当天广告
include(识别顾客)
include(验证账户)
-------------------------- <可能的事务>(扩展点1)
打印凭条标题
-------------------------- <凭条细节>(扩展点2)
登出
扩展用例取款:
收到取款请求
指定取款数量
-------------------------- <产生的请求>(扩展点3)
付款
看到这里,我们就不难解释为什么图2把ATM会话与取款之间的关系画成扩展了(注:这本身可能不符合亚克申扩展用例的原则[4])。由于在身份验证通过后,顾客具体执行什么操作是不定的,可以是取款、存款、转账、查询等事务中的任何一个,将来甚至还有可能添加(扩展)新的功能,而ATM打印的凭条根据用户操作的不同,内容也将有所不同。这些内容的发生都需要具备一定的条件,属于可能的情况,这一点似乎很符合扩展用例的定义。考虑到这些因素,作者于是采用了图2的策略,把可能的操作和打印内容都从基用例中抽走。
然而不要忘了,除了扩展用例只有在特定条件下才能被触发之外,把一段内容提取为扩展用例还要求即使在没有该扩展用例插入的情况下,基用例本身的执行也不应受到任何影响。因此,如果我们把上段文字中的两个扩展点拿掉(只看左边的文字)并参照寇本目标用例的标准,可以发现该基用例完全不是一个目标得以完整执行的用例。上述写法是有问题的,它不完整,而且效率不高,似乎模仿了一些编程语言的写法和思路,在实践中不值得推广。
下面再让我们看看寇本版的“使用ATM”[1]:
1. 顾客插卡,输入PIN。
2. ATM验证顾客账户和PIN。
3. 顾客执行下列任一事务:
取款
存款
转账
查询
顾客执行以上事务直到选择退出。
4. ATM退卡。
在这里,取款、存款等事务操作明确地出现在用例的基本流中,显然它们都是被“使用ATM”所包含的用例。注意,在寇本用例中每个动作步骤不一定都是顺序执行的,可以有循环,有选择,甚至可以是任意顺序,例如上面的步骤3。在WEUC中寇本还指出,实际上取款、存款、转账等操作都是顾客执行一次交易事务的特例和具体化,所以还可以进一步画成类似图3的形式。。我们可以看到图1其实是图3的简化。
图 3、更为准确的画法
应该如何判定一个用例关系是包含关系还是扩展关系?有些情况还是比较明确的,例如对于“验证身份”和“吞卡”,大家可能都毫无异议,一致认为它们分别是“使用ATM”的包含用例和扩展用例。但是,一旦出现上例的情况时,就较难处理了。不过,我们通常可以据此判定:如果触发条件中含有基用例负责的事物,即基用例知道附加用例何时、何处、为什么发生,那么基用例应该包含(调用或引用)它;如果触发条件中含有附加用例负责的事物,即附加用例知道它应该何时、何处、为什么发生,那么应该让附加用例扩展基用例[1]。这两条规则还是很管用的。此外,根据笔者经验,还可以参考这样一条简单法则:凡是在用例基本流中出现的附加用例都应作为包含用例,而在扩展流中出现的附加用例必然是扩展用例。显然,图3符合以上规则。
问题似乎得以解决了。然而,在UML 2.0上层结构规范草中,我们却看到了类似图4的方案:
图4、另一种近似的画法
有人要问了,为什么针对同一段用例文本描述,却存在两种矛盾的UML画法,同一个用例“取款”在图3中是包含,在图4中却是继承,到底哪个是对的?其实,图4与图3这两种用例模型在整体上所描述的内容是完全一致的,问题就出在用例名称上。分析、比较用例之间的关系,请务必关注用例的内容——动作步骤。我们可以发现,图4中的“取款”用例与图3中的“取款”用例所包含的实质内容是不一样的,一大,一小。所以,假使我们把它们的名称分别改为“取款交易”和“取款(事务)操作” ,并约定前者“大于”后者,就可以在一定程度上消除二义性,这样两个图就近似等价了。不管如何,图3、图4都一致地没有把“取款”、“存款”、“转账”等用例作为基用例的扩展。笔者认为,经过改进,这两种画法目前都是可行的,而图3可能更好一些。
关于用例包含、扩展和继承关系方面的更深层探讨,值得写一篇专述文章,在这里不可能展开。在目前众说纷纭的情况下,建议大家像寇本建议的那样,如果实在搞不清两个用例到底是什么关系,不如干脆都先定为包含关系,这样做并不影响后续的系统分析设计和使用,因为用例本身的内容写得如何才是问题的实质、真正的关键。总之,实践者不要指望只通过看几个用例的名字就能准确地判断出它们之间的关系。可靠的依据来自于用例内部,不必在弄清用例的关系上浪费太多时间,这个问题可以留待学者和研究人员来解决。
2.4.2 sub use case之争
寇本在WEUC中把包含用例称为sub use case,即subordinate use case之意,但其实subordinate use case(笔者将其译为“分用例”)在亚克申方法中有专门的用途(在亚克申博士早就提出的对大规模系统建模的SystemOf InterconnectedSystems模式中,有超系统/超用例和分系统/分用例之分[4])。而寇本采用的sub use case叫法很容易被直译成中文“子用例”,从而造成新的误解,因为我们知道“父子”一说在OO方法中通常是用来形容对象类之间的继承关系的,子类是父类的派生类,显然这与包含用例的真实语义不符。
在UML[10]中,把包含用例、扩展用例统称为附加(additional)用例,被包含、被扩展的用例叫做基用例,在用例的继承关系中则采用父用例、子用例的说法,这样做是妥当的。
UUCM:
建议在实践中尽量回避寇本sub use case的说法,可以用“包含用例”或“附加用例”来代称,同时明确约定在中文中凡是提到“子用例”的地方,就是指用例的继承或一般化。
2.5 Actor
寇本用例的Actor类型有7种之多,包括[1]:
Actor(something with behavior)、
External actor(an actor outside SuD)、
Stakeholder(an external actor entitled to have its interests protected by the system)、
Primary actor(a stakeholder who requests that the system deliver a goal)、
Supporting/secondary actor(a system against which the SuD has a goal)、
Offstage/tertiary actor(a stakeholder who is not the PA)、
Internal actor(either the SuD, a subsystem or an active component of the SuD)。
RUP关于Actor的定义[7]是: Someone or something, outside the system or business that interacts with the system or business。UML的定义是: An actor specifies a role played by a user or any other system that interacts with the subject。
UUCM:
亚克申用例与寇本用例对于Actor的定义存在着明显区别。亚克申及UML的Actor是系统之外的人或物,而寇本Actor的含义(可能来自早期的亚克申版本)比较笼统,其范围要大得多,有内外Actor之分。既然我们确定Actor的主要目的之一是为了划定系统(或业务)的边界,那么应该始终把Actor当作系统外部的事物,这样才比较妥当,内部Actor是不必要的概念。而且,在实际应用中Actor即重要也不重要[1],识别Actor的主要目的是为了帮助提取用例,通常不需要对各种Actor类型加以如此细致的区分。
另外,寇本关于stakeholder的定义与通常的理解也有所差别。寇本认为stakeholder是一种外部Actor(注:这本身与寇本自己对用例的定义明显存在矛盾[1]),这不对。按常理,所有利益相关人包括内部Actor、外部Actor都应该属于stakeholder之列,尤其当我们讨论的是业务组织的时候。
2.6后置条件
与亚克申用例相比,寇本对用例的后置条件进行了细分,提出了最小保证、成功保证等概念,这样做是有意义的。最小保证描述了系统不管在任何情况下,尤其当用例失败、目标未达成时,都应满足的起码条件和应采取的措施。
UUCM:
建议采纳寇本方法的最小保证和成功保证划分。此外,在前置、后置条件(最小保证和成功保证)中除了说明必须满足的条件外,还可以分别说明系统在用例开始前和结束后的状态,包括各种成功和失败状态以及对失败状态的处理。
2.7动作步骤
寇本用例步骤采用独特的编号方式,基本流采用1,2,3…顺序编号,扩展流的条件和扩展步骤采用数字、字母间隔的方式,如1a、1a1、5c、5c3b1等等,而且还可以使用宏代符*,可以指定任意数目步骤的条件,如1-9a、2,7-9c等等,使用起来非常方便。RUP的基本流、扩展流步骤完全采用自然分节的顺序编号,如1.1、2.3.4.1等等,不便于阅读者找到用例具体的引用位置,在指定扩展位置时显得较为麻烦。
另一方面,RUP用例的每个步骤都可以附上一个名称,这叫做“命名步骤”。
如果一个步骤内容较多,用一个短语标记来概括说明该步骤执行的大致内容,确实比较方便,而且将来需要画用例的活动图时也可以作为快速参考。
UUCM:
建议采纳寇本的步骤编号方式和RUP的命名步骤方法。
2.8文本与UML
亚克申用例方法与UML、RUP三者之间有着天然的紧密联系。用例驱动、可视化建模是RUP的两大特征,若用例和UML缺席,RUP也就称不上RUP了。在亚克申方法中,除了可以用RUP的格式文本描述用例外,还推荐适当地选择利用UML用例图、序列图、协作图、活动图和状态图5种图示从各个角度来描述用例,可谓手段充足、武器齐备。
寇本代表作WEUC的主基调是用结构化/半结构化的文本描述用例。虽然他也提到了UML,但讨论主要集中在如何正确对待UML的用例图和相关CASE工具等问题上,对UML用于描述动态行为的其他4种图的作用和意义强调得不够充分,其实这些图的作用远比用例图要重要得多,也是UML强大描述能力之体现。
UUCM:
如前所述,文本用例本质上是对对象交互过程的执行步骤的罗列。用例本身即每个“椭圆”内部的东西才是最为关键和重要的,用例之间的关系相对来说要次要些,在这一点上,可以说亚克申方法和寇本方法的看法是一致的[1][4]。实践中,我们发现很多时候,先写文本系统用例,后照着已有的文字说明画UML图比先画图或完全依赖于图形来描述系统用例要容易得多。可见对于软件需求分析,我们应该首先把更多的精力放在写好文本用例上(这正是寇本方法的强项)。
但是,寇本认为“Sequence diagrams are not a good form for expression use cases”[1],这种说法有失准确和全面。纵观全书,寇本主要是从工具使用的角度来分析的,他在书中对当时那些不那么先进的CASE工具颇有微词,认为它们不如文本写作更加便捷和有效。然而,事实上这只是问题的一个方面,UML工具的缺陷并不能简单地等同于UML语言本身的缺陷。正确地使用UML及其工具不仅仅是为了获得形象直观、交叉引用、超链接、名称自动变更等一目了然的好处,更重要的一个原因是,通过合适的UML图形,比如SSD(系统序列图),来精确地定义和描述系统事件与作为其响应的系统操作(也就是系统的输入和输出)[8]之间的契约,这正是后续系统分析和设计的起点。序列图在用例分析中其实起到相当关键的作用,在实践中用它来刻画每一个重要的系统用例也是非常普遍的做法。
而且,在分析复杂的业务流程/业务用例时,人们好像更习惯于首先画活动图(可能与人类自身的思维习惯有关),反而不太愿意采用繁琐的文字说明。对于复杂和关键的用例,除了一些必要的文本描述之外,再辅之以UML活动图、序列图或状态图进行可视化,是行之有效的好做法,有助于澄清问题本质、迅速抓住要领。对于复杂的用例模型,通过用例图描述用例之间的关系,提供全局浏览视图,也是非常必要的。
用例的UML图形与文本描述之间不是谁取代谁的关系,而是相辅相成、优势互补,应该因地制宜地加以运用。不仅如此,同时拥有用例的结构/半结构化文
本和UML图形,往往还有助于彼此之间相互比对、确认,能显著提高用例描述和分析的正确性。根据本人经验,把两者结合起来运用效果才是最好的,没有必要过份地强调某一方面。
2.9黑盒白盒
亚克申博士发明的用例实现(UCR,Use Case Realization)[5]在RUP、UML中是一个非常重要的概念,它描述了内部对象如何相互协作共同实现一个用例,理论上每一个用例都应该至少有一个UCR与其对应,因而UCR在亚克申方法中起到了联接问题域和解决域、贯穿整个软件分析设计过程的关键作用。在寇本方法中,不存在UCR这个术语,只有黑盒用例(需求)、白盒用例之分(需求的实现)。
UUCM:
首先,既然谈及基于用例表示的功能需求,那么它就应该是黑盒的、透明的。如果我们看到了系统内部的情况(白盒、不透明),那么这其实已经是需求的一种实现了。所以,我们应该在需求分析时尽量避免“白盒用例”这种矛盾的说法。严格地区分UC和UCR,有助于项目团队在实践中消除需求和实现不分的情况。这一点过去在被拉来写需求的程序员当中比较普遍,受习惯性思维的影响,他们往往写到最后就变成写软件设计方案了,这是很糟糕的。
2.10格式模板
寇本在WEUC中一共列出了5种主要的用例格式模板:完整型、简易型、单列表式、双列表式和RUP样式。在此,我们推荐以完整型(寇本本人最喜欢的)为基础,结合了单列表式和RUP样式特点的UUCM模板:
用例名称:
层次:
+ | ! | -
范围:
简述/背景:
主使用者及利益:
其他受益人及利益:
受益人1:
受益人2:
最小保证:
状态1:
状态2:
后置
条件
成功保证:
前置条件:
条件1:
条件2:
状态1:
状态2:
触发事件:
基本流:
1. 步骤1
2. <可选名称>.步骤2
…
n. 步骤n
<结束>
扩展流:
1a. 条件1:
1a1. 步骤1
1a2. 步骤2
扩展点:
名称1:位置1
名称2:位置2
技术和数据变化:
1a.
2a.
非功能需求:
业务规则:
备注:
其他必要字段
……
表1、UUCM模板v1.0
双列表式较多地被用来描述用户界面需求,有些人偏爱它,但我们发现双列表不够简洁,比较占空间,而且很多情况并不适用,比如参与者多于两个的情况。
三、结语
寇本用例方法以基于目标的结构化/半结构化文本描述见长,亚克申用例方法更重视UML可视化建模和用例驱动过程。两者尽管同宗同源[1],却在一些使用细节上存在着明显差异,而且各自还在沿着既有轨道继续向前发展。 本文提出的统一用例方法并不是一种全新的方法,UUCM仅仅是个符号或代称(也许可以有其他更好的名称),它实质上代表了一种解决方案和思路,目的是吸收亚克申和寇本用例方法的长处,消除两者的不一致,从而帮助实践者尽可能规避使用上的误区,发挥用例和UML方法“1+1〉2”的联合优势。这既是实践的选择,也是现实的需要。
话说天下IT之势,合久必分,分久必合。UUCM不是句号,而是一个新的起点。出于商业、私人或其他方面的原因,国内外许多技术流派分呈的局面会长久持续下去,这本身是一件好事。不过,对于我们实践者来说,选择实用的工程技术时不应有门派分割的障碍。对于特定的场合、特定的项目,企业人往往只有一个明智的选择,那就是:用最小的成本创造最大的客户价值。所以,对于实用型技术我们完全可以采取拿来主义的态度,防止guru-locked-in,适用的即是最好的!
posted @ 2010-03-04 14:03 Silver.Lee 阅读(459) 评论(0) 编辑
统一建模语言UML轻松入门之用例
目前,在的内地版《神雕侠侣》中,杨过和小龙女有一份不为人知的默契与浪漫,那就是他们所绘制的并肩小人图。这样的小人图,是UML用例图的一部分,被称为参与者。
2.1 用例与用例图
用例是需求分析中最重要的概念,需求表征了一个系统的设计特性、特征和行为,描述一个系统的需求意味着描述了建立在该系统外部的事物与系统之间的契约,契约上声明了期望系统做什么。
需求获取(Requirement Elicitation) 是需求工程的主体,其主要工作是建立待开发系统的模型,而用例就是用于建立这种模型的良好方法。用例最初由Ivar Jackboson博士提出,后被综合到UML规范之中,成为需求表述的标准化体系。前文已经提到,整个RUP流程都是"用例驱动"的,各种类型的开发活动包括项目管理、分析、设计、测试、实现等以用例为主要输入工件,用例模型奠定了整个系统软件开发的基础,用例被认作第二代面向对象技术的标志,可见其重要性非同一般。
我们先来给出一个具体而简单的用例图,即"图书管理系统"用例图,如图2.1。在用例图中主要涉及到参与者(又称角色、执行者)、用例以及二者之间的通讯关联。
图2.1 图书管理系统用例图
参与者
参与者是与系统、子系统或类发生交互的外部用户、进程或其他系统。参与者可以是人、另一个计算机系统或一些可运行的进程。在图2.1中,"读者"和"管理员"即为参与者。
参与者之间可以存在泛化关系,例如,在图2.1所示图书馆管理系统用例图中,可以认为"读者"是"学生读者"和"教师读者"的泛化,而"学生读者"还可以具体化为"本科生读者"和"研究生读者";同样,"图书管理人员"也是"采购员"、"编目员"及"借阅人员"的泛化。图2.2表示出了参与者之间的泛化关系。
图2.2 参与者泛化关系
用例
用例是外部可见的一个系统功能,这些功能由系统所提供,并通过与参与者之间消息的交换来表达。用例的用途是在不揭示系统内部构造的情况下定义行为序列,它把系统当作一个黑箱,表达整个系统对外部用户可见的行为。
鉴于用例的特点,用例一般被命名为一个能够说明目标的动名词组。如图2.1中的"借书"、"还书"和"管理图书"皆为动名词组。
用例之间也可以存在包含、扩展和泛化等关系:
(1)包含关系:用例可以简单地包含其他用例具有的行为,并把它所包含的用例行为做为自身行为的一部分,这被称作包含关系。
(2)扩展关系:扩展关系是从扩展用例到基本用例的关系,它说明为扩展用例定义的行为如何插入到为基本用例定义的行为中。它是以隐含形式插入的,也就是说,扩展用例并不在基本用例中显示。在以下几种情况下,可使用扩展用例:
a.表明用例的某一部分是可选的系统行为(这样,您就可以将模型中的可选行为和必选行为分开);
b.表明只在特定条件(如例外条件)下才执行的分支流;
c.表明可能有一组行为段,其中的一个或多个段可以在基本用例中的扩展点处插入。所插入的行为段和插入的顺序取决于在执行基本用例时与主角进行的交互。
图2.3给出了一个扩展关系的例子,在还书的过程中,只有在例外条件(读者遗失书籍)的情况下,才会执行赔偿遗失书籍的分支流。
图2.3用例扩展关系
(3)泛化关系:用例可以被特别列举为一个或多个子用例,这被称做用例泛化。当父用例能够被使用时,任何子用例也可以被使用。如在图2.4中,订票是电话订票和网上订票的抽象。
图2.4用例泛化关系
通讯关联
通讯关联用于表示参与者和用例之间的对应关系,它表示参与者使用了系统中的哪些用例(或者说系统所提供的用例被哪些参与者使用)。
通讯关联以箭头或实线表示。若使用箭头,箭头所指方将是对话的被动接受者;如果不强调对话中的主动与被动关系,则可以使用不带箭头的关联实线。
2.2建立用例模型
知道了用例与用例图的概念,我们还需要懂得怎样建立用例模型,即怎样找出参与者、用例以及定义用例的过程。一般来说,建立用例模型的步骤为:
(1)确定谁会直接使用该系统,即参与者(Actor),为了发现参与者,我们可以尝试问如下问题:
a. 谁/什么使用系统?
b. 谁/什么从系统获得信息?
c. 谁/什么向系统提供信息?
d. 谁/什么支持、维护系统?
e. 哪些其它系统使用此系统?
f. 公司的哪个部门使用系统?
…
(2)选取其中一个参与者;
(3)定义该参与者希望系统做什么,参与者希望系统做的每件事成为一个用例,为了发现用例,我们可以尝试问如下问题:
a. 为什么该参与者想要使用此系统?
b. 该参与者是否要创建、保存、更改、移动或读取系统的数据?如果是,为什么?
c. 该参与者是否要通知系统外部事件或变化?
d. 该参与者是否需要知道系统内部的特定事件?
…
(4)对每件事来说,何时参与者会使用系统,通常会发生什么,这就是用例的基本过程;
(5)描述该用例的基本过程;
(6)考虑一些可变情况,把他们创建为扩展用例;
(7)复审不同用例的描述,找出其中的相同点,抽出相同点作为共同的用例;
(8)重复步骤2-7找出每一个用例。
参与者检查的参考标准如下:
(1)是否您已找到所有的参与者?也就是说,是否您已经对系统环境中的所有参与者都进行了说明和建模?
(2)每个参与者是否至少涉及到一个用例?
(3)您能否列出至少两名可以作为特定参与者的人员?
(4)是否有参与者担任与系统相关的相似参与者?如果有,您应该将他们合并到一个参与者中。
用例检查的参考标准如下:
(1)用例模型的简介部分简明清晰地概述此系统的目的和功能;
(2)所有的用例已确定,这些用例共同说明所有的必要行为;
(3)所有的功能性需求都至少映射到一个用例;
(4)该用例模型不包含多余的行为,所有的用例都可回溯到某个功能性需求来证明其合理性。
用例图从总体上大致描述了系统所能提供的各种服务,让我们对于系统的功能有一个总体的认识,仅此还是不够的,我们还需要描述每一个用例的详细信息,即用例规约。用例模型正是由用例图和每一个用例的详细描述――用例规约所组成的。RUP中提供了用例规约的模板,包含以下内容:
(1)简要说明 (Brief Description):简要介绍该用例的作用和目的;
(2)事件流 (Flow of Event):包括基本流和备选流,事件流应该表示出所有的场景;
(3)用例场景 (Use-Case Scenario) :包括成功场景和失败场景,场景主要是由基本流和备选流组合而成的;
(4)特殊需求 (Special Requirement):描述与该用例相关的非功能性需求(包括性能、可靠性、可用性和可扩展性等)和设计约束(所使用的操作系统、开发工具等);
(5)前置条件 (Pre-Condition):执行用例之前系统必须所处的状态;
(6)后置条件 (Post-Condition):用例执行完毕后系统可能处于的一组状态。
用例规约基本上是用文本方式来表述的,为了更加清晰地描述事件流,也可以选择使用状态图、活动图或序列图来辅助说明(状态图有助于描述与状态相关的系统行为,活动图有助于描述复杂的决策流程,序列图适合于描述基于时间顺序的消息传递)。另外,只要对简洁明了地表达用例有帮助,我们就可以在用例中任意粘贴用户界面、流程的图形化显示方式及其他图形。
posted @ 2010-03-04 14:01 Silver.Lee 阅读(101) 评论(0) 编辑
用例建模技巧
从参与者的角度并以主动语态编写用例。
应该以主动语态:“学生表明参加研习班意向”,而不是被动语态“研习班意向被学生表明”来编写用例。而且,应该从参与者的角度来编写用例。毕竟,用例的目的是理解用户如何对系统进行操作。
编写方案文本,而非功能需求。
用例描述的是对参与者来说有价值的一系列行动,而不是特性集。例如,“招收研习班的学生”用例描述的是学生如何与系统交互来参加研习班。它没有描述用户界面看上去是什么样子,或者它是如何工作的。有一些其它的模型来描述这些重要的信息,例如用户界面模型和增补规范。面向对象分析非常复杂,因此需要对它使用几种模型,并且应该适当地应用每一种模型。
用例只记载行为需求。
用例既不是类规范,也不是数据规范。这是应该由概念性模型捕捉的一种信息,在对象世界中,它是通过 UML 类模型建模的。您往往会引用概念性模型中描述的类,例如,“参加研习班”用例包括了“研习班”和“学生”等概念,它们都将由概念性模型描述。
不要忘记用户界面。
系统用例经常引用主用户界面 (UI) 元素,这些元素常常称为“边界”或“用户界面”项,例如 HTML 页面和报表。用例有时也引用一些次要的 UI 元素,例如按钮或数据输入字段,但这种级别的细节并不太常见。
创建用例模板。
用例包含了相当数量的信息,这些信息可以轻易地以常见格式记载。您应该考虑开发自己的模板(请参阅技巧“记载用例”)。
始终如一地组织用例图。
一般的做法是垂直地绘制继承 (inheritance) 和扩展 (extend) 关联,在父/基本用例下面绘制继承/扩展用例。同样,通常水平绘制包含 (include) 关联。请注意,这些是简单的经验法则 -- 只要始终遵循这些法则,产生的图将很容易理解。
不要忘记系统对参与者行动的响应。
用例既应该描述参与者是如何与系统交互的,也应该描述系统如何响应这些交互。例如,在“参加研习班”用例中,如果系统在学生表明他们希望参加研习班时没有做出响应,学生就会很沮丧地离开。
备选行动过程非常重要。
如果一切顺利,使用的将是基本行动过程 -- 但也不要忘记备选过程。引入备选过程是为了描述潜在的使用错误以及商业逻辑错误和异常。这些重要的信息对于驱动系统的设计来说很有必要,因此不要忘记在用例中对它们建模。
不要被 <<include>> 和 <<extend>> 关联所困扰。
我不是很确定到底发生了什么事,但我总是在想包含 (include) 和扩展 (extend) 关联,以及旧版本 UML 中使用 (uses) 和扩展 (extends) 关联的正确使用从来没有得到很好的描述。结果,用例建模小组往往在这些关联的正确应用上争论不休,在整个建模技术中一些有趣但次要的部分上浪费了惊人的时间。我曾在一个组织中工作,这家组织居然取缔了 <<include>> 和 <<extend>> 原型的使用,几个星期后,当意识到公司仍然需要这些概念时不得不撤消了这种极端的解决方案,而这时该组织对它们的正确使用还没有达成共识。
让用例带动用户文档。
用户文档的目的是描述如何使用系统。每个用例都描述了参与者通过使用系统所采取的一系列动作。简而言之,用例包含从中开始编写问党用户稳当的信息。例如,可以使用“参加研习班”用例作为基础来编写系统用户文档的“如何参加研习班”一节。
让用例带动演示。
软件开发过程中的一部分是向项目资金管理者通报工作成果,因此有时需要提供演示。因为用例是从用户的角度编写的,它们包含了演示中对资金管理者可能希望听到的事物的有价值的深刻见解。换句话说,用例通常包含制定演示稿所需的逻辑。
参考资料 The Object Primer 2nd Edition,由 Scott W. Ambler 著。New York: Cambridge University Press, 2001。
The Unified Process Inception Phase,由 Scott W. Ambler 和 Larry L. Constantine 合著。Gilroy, CA: R&D Books, 2000。
Software For Use: A Practical Guide to the Models and Methods of Usage-Centered Design,由 Larry L. Constantine and Lucy A.D. Lockwood 合著。
The Unified Modeling Language Reference Manual,由 James Rumbaugh、Grady Booch 和 Ivar Jacobson 合著。Reading, MA: Addison-Wesley Longman, Inc., 1999。
posted @ 2010-03-04 14:00 Silver.Lee 阅读(67) 评论(0) 编辑
2010年2月22日
SQL Server不允许进行远程连接的解决办法
刚刚安装的数据库系统,按照默认安装的话,很可能在进行远程连接时报错,通常是错误:"在连接到SQLServer2005时,在默认的设置下SQLServer不允许进行远程连接可能会导致此失败。(provider:命名管道提供程序,error:40-无法打开到SQLServer的连接)",现在针对此问题总结如下:
明白了SQLServer是个网络数据库就可迎刃而解了,简单的分为下面的几种情况。
◆1.数据库引擎没有启动。有两种启动方式:
(1)开始->程序->MicrosoftSQLServer2005->SQLServer2005外围应用配置器,在打开的界面单击"服务的连接的外围应用配置器",在打开的界面中找到DatabaseEngine,单击"服务",在右侧查看是否已启动,如果没有启动可单击"启动",并确保"启动类型"为自动,不要为手动,否则下次开机时又要手动启动;
(2)可打开:开始->程序->MicrosoftSQLServer2005->配置工具->SQLServerConfigurationManager,选中SQLServer2005服务中SQLServer(MSSQLSERVER),并单击工具栏中的"启动服务"按钮把服务状态改为启动;
使用上面两种方式时,有时候在启动的时候可能会出现错误,不能启动,这时就要查看"SQLServer2005配置管理器"中的SQLServer2005网络配置->MSSQLSERVER协议中的VIA是否已启用,如果已启用,则把它禁止.然后再执行上述一种方式操作就可以了.
◆2.是否已经允许远程连接。这个部分可以简单的分为4个方面,分别是在SQLServer上启用远程连接、启用SQLServer浏览服务、在Windows防火墙中为SQLServer2005创建例外和在Windows防火墙中为“SQLBrowser”创建例外。下面是几个具体的操作方式:
在SQLServer实例上启用远程连接
◆1.指向“开始->程序->MicrosoftSQLServer2005->配置工具->SQLServer外围应用配置器”
◆2.在“SQLServer2005外围应用配置器”页,单击“服务和连接的外围应用配置器”
◆3.然后单击展开“数据库引擎”,选中“远程连接”,在右边选中“本地连接和远程连接”,
再选择要使用的协议,(这个地方应当启用TCP/IP和命名管道服务!)单击“应用”,您会看到下消息:
“直到重新启动数据库引擎服务后,对连接设置所做的更改才会生效。”,单击“确定”按钮返回
◆4.展开“数据库引擎”,选中“服务”,在右边单击“停止”,等到MSSQLSERVER服务停止,
然后单击“启动”,重新启动MSSQLSERVER服务。
启用SQLServer浏览器服务
◆1.指向“开始->程序->MicrosoftSQLServer2005->配置工具->SQLServer外围应用配置器”
◆2.在“SQLServer2005外围应用配置器”页,单击“服务和连接的外围应用配置器”
◆3.然后单击展开“SQLServerBrowser”,选中“服务”,在右边“启动类型”选择“自动”,
再单击“启动”,单击“确定”按钮返回
在Windows防火墙中为“SQLServer2005”创建例外
◆1.在Windows防火墙设置界面中,选择“例外”选项卡,然后单击“添加程序”
◆2.在“添加程序窗口”中单击“浏览”
◆3.然后找到“C:ProgramFilesMicrosoftFilesMicrosoftSQLServerMSSQL.1MSSQLBinnsqlservr.exe”,
单击“确定”返回
注意:路径可能会根据SQLServer2005安装不同而不同。MSSQL.1是占位符,对应数据库实例ID。
◆4.对每个需要打开远程访问的SQLServer2005实例,重复步骤1至3。
在Windows防火墙中为“SQLBrowser”创建例外
◆1.在Windows防火墙设置界面中,选择“例外”选项卡,然后单击“添加程序”
◆2.在“添加程序窗口”中单击“浏览”
◆3.然后找到“C:ProgramFilesMicrosoftFilesMicrosoftSQLServer90Sharedsqlbrowser.exe”,
单击“确定”返回
注意:路径可能会根据SQLServer2005安装不同而不同。在使用.NET开发进行时,会遇到使用连接字符串连接SQLServer2005数据库使用机器名称和localhost都能连接,但是使用IP地址却不能连接的问题,解决的办法是在SQLServer实例上启用本地和远程连接,并且在选择协议的时候使用TCP/IP和命名管道服务即可解决。
posted @ 2010-02-22 15:39 Silver.Lee 阅读(31) 评论(0) 编辑
2010年2月5日
为什么要使用代理模式
代理这个词大家肯定已经非常熟悉,因为现实中接触的很多,其实现实中的东西恰恰可以非常形象和直观地反映出模式的抽象过程以及本质。现在房子不是吵得热火朝天吗?我们就以房子为例,来拨开代理的面纱。
假设你有一套房子要卖,一种方法是你直接去网上发布出售信息,然后直接带要买房子的人来看房子、过户等一直到房子卖出去,但是可能你很忙,你没有时间去处理这些事情,所以你可以去找中介,让中介帮你处理这些琐碎事情,中介实际上就是你的代理。本来是你要做的事情,现在中介帮助你一一处理,对于买方来说跟你直接交易跟同中介直接交易没有任何差异,买方甚至可能觉察不到你的存在,这实际上就是代理的一个最大好处。
接下来我们再深入考虑一下为什么你不直接买房子而需要中介?其实一个问题恰恰解答了什么时候该用代理模式的问题。
原因一:你可能在外地上班,买房子的人没法找到你直接交易。
对应到我们程序设计的时候就是:客户端无法直接操作实际对象。那么为什么无法直接操作?一种情况是你需要调用的对象在另外一台机器上,你需要跨越网络才能访问,如果让你直接coding去调用,你需要处理网络连接、处理打包、解包等等非常复杂的步骤,所以为了简化客户端的处理,我们使用代理模式,在客户端建立一个远程对象的代理,客户端就象调用本地对象一样调用该代理,再由代理去跟实际对象联系,对于客户端来说可能根本没有感觉到调用的东西在网络另外一端,这实际上就是Web Service的工作原理。另一种情况虽然你所要调用的对象就在本地,但是由于调用非常耗时,你怕影响你正常的操作,所以特意找个代理来处理这种耗时情况,一个最容易理解的就是Word里面装了很大一张图片,在word被打开的时候我们肯定要加载里面的内容一起打开,但是如果等加载完这个大图片再打开Word用户等得可能早已经跳脚了,所以我们可以为这个图片设置一个代理,让代理慢慢打开这个图片而不影响Word本来的打开的功能。申明一下我只是猜可能Word是这么做的,具体到底怎么做的,俺也不知道。
原因二:你不知道怎么办过户手续,或者说除了你现在会干的事情外,还需要做其他的事情才能达成目的。
对应到我们程序设计的时候就是:除了当前类能够提供的功能外,我们还需要补充一些其他功能。最容易想到的情况就是权限过滤,我有一个类做某项业务,但是由于安全原因只有某些用户才可以调用这个类,此时我们就可以做一个该类的代理类,要求所有请求必须通过该代理类,由该代理类做权限判断,如果安全则调用实际类的业务开始处理。可能有人说为什么我要多加个代理类?我只需要在原来类的方法里面加上权限过滤不就完了吗?在程序设计中有一个类的单一性原则问题,这个原则很简单,就是每个类的功能尽可能单一。为什么要单一,因为只有功能单一这个类被改动的可能性才会最小,就拿刚才的例子来说,如果你将权限判断放在当前类里面,当前这个类就既要负责自己本身业务逻辑、又要负责权限判断,那么就有两个导致该类变化的原因,现在如果权限规则一旦变化,这个类就必需得改,显然这不是一个好的设计。
好了,原理的东西已经讲得差不多了,要是再讲个没完可能大家要扔砖头了。呵呵,接下来就看看怎么来实现代理。
代理模式的实现:
其实代理模式还是很容易实现的,随便举个例子,比如你有一个类负责返回员工的薪资信息,如下:
Java代码
publicclassBusinessClass
{
publicdoubleGetPayroll(stringemployee)
{
//返回薪资结果
return1000;
}
}
由于薪资信息是公司的机密信息,不是谁都能调用查看,所以我们为该类做一个代理来做用户身份的验证,代码如下:
publicclassProxy
{
privateBusinessClassbc;
publicProxy(BusinessClassbc)
{
this.bc=bc;
}
publicdoubleGetPayroll(stringuser)
{
//判断user权限
//如果不符合返回null,或者抛出异常
if(IsManage(user))
{
returnbc.GetPayroll(”张三”);
}
thrownewException(”你没有该权限。”);
}
}
注意:代理类需要使用被代理类来做业务逻辑,所以代理类需要包含被代理类的实例,这跟适配器模式是一样的。
到目前为止目的其实已经达到了,但是现实中我们常常会再为代理类和被代理类抽象出一个公共接口,如下:
publicinterfaceIBusinessClass
{
doubleGetPayroll(stringuser);
}
很多人肯定会问为什么要抽象这个接口呢?其实抽象接口有一个最大的原因就是约束双方的行为!什么意思呢?其实就是我逼迫Proxy必须实现某些方法,而这些方法恰恰是对外公开的主要业务方法。当然也可以靠程序员自律,但是多一个约束总归是好的,至少如果没有实现指定方法我们可以在编译期就发现错误,总比执行时才能发现错误要好。另外的原因可能都得归结到使用接口的好处上去了,这里不再赘述,自己去查接口的文章介绍了。
posted @ 2010-02-05 17:48 Silver.Lee 阅读(560) 评论(0) 编辑
Single Responsibility Principle (SRP) - OO设计的单一职责原则
前言
Robert C. Martin氏为我们总结了在面向对象的设计(OOD)中应该遵循的原则,这些原则被称为“Principles of OOD”,关于“Principles of OOD”的相关文章可以从Object Menter 得到。
本文介绍“Principles of OOD”中的单一职责原则:Single Responsibility Principle (SRP)。
可以从这里查看Single Responsibility Principle (SRP)的原文 。
概要
There should never be more than one reason for a class to change.
永远不要让一个类存在多个改变的理由。
换句话说,如果一个类需要改变,改变它的理由永远只有一个。如果存在多个改变它的理由,就需要重新设计该类。
SRP(Single Responsibility Principle)原则的核心含意是:只能让一个类有且仅有一个职责。这也是单一职责原则的命名含义。
为什么一个类不能有多于一个以上的职责呢?
如果一个类具有一个以上的职责,那么就会有多个不同的原因引起该类变化,而这种变化将影响到该类不同职责的使用者(不同用户):
1,一方面,如果一个职责使用了外部类库,则使用另外一个职责的用户却也不得不包含这个未被使用的外部类库。
2,另一方面,某个用户由于某个原因需要修改其中一个职责,另外一个职责的用户也将受到影响,他将不得不重新编译和配置。
这违反了设计的开闭原则,也不是我们所期望的。
职责的划分
既然一个类不能有多个职责,那么怎么划分职责呢?
Robert.C Martin给出了一个著名的定义:所谓一个类的一个职责是指引起该类变化的一个原因。
If you can think of more than one motive for changing a class, then that class has more than one responsibility.
如果你能想到一个类存在多个使其改变的原因,那么这个类就存在多个职责。
Single Responsibility Principle (SRP)的原文 里举了一个Modem的例子来说明怎么样进行职责的划分,这里我们也沿用这个例子来说明一下:
SRP违反例:
Modem.java
interface Modem {
public void dial(String pno);//拨号
public void hangup();//挂断
public void send(char c);//发送数据
public char recv();//接收数据
}
咋一看,这是一个没有任何问题的接口设计。但事实上,这个接口包含了2个职责:第一个是连接管理(dial, hangup);另一个是数据通信(send, recv)。很多情况下,这2个职责没有任何共通的部分,它们因为不同的理由而改变,被不同部分的程序调用。
所以它违反了SRP原则。
下面的类图将它的2个不同职责分成2个不同的接口,这样至少可以让客户端应用程序使用具有单一职责的接口:
让ModemImplementation实现这两个接口。我们注意到,ModemImplementation又组合了2个职责,这不是我们希望的,但有时这又是必须的。通常由于某些原因,迫使我们不得不绑定多个职责到一个类中,但我们至少可以通过接口的分割来分离应用程序关心的概念。
事实上,这个例子一个更好的设计应该是这样的,如图:
小结
Single Responsibility Principle (SRP)从职责(改变理由)的侧面上为我们对类(接口)的抽象的颗粒度建立了判断基准:在为系统设计类(接口)的时候应该保证它们的单一职责性。
更多阅读
天之裂片怎么刷?天之裂片有什么用 6.0天之裂片怎么刷
天之裂片怎么刷?天之裂片有什么用——简介你是否看到过一条红色的云端祥龙在锦绣谷上方飞来飞去,它还带着一个buff:刀枪不入,不错,只要你能破了它的刀枪不入,你就可以杀它了,杀死它之后就会掉落坐骑。天之裂片怎么刷?天之裂片有什么用——
思科模拟器:2 入门之界面简介
思科模拟器:[2]入门之界面简介——简介思科模拟器是网络工程师经常使用的网络实验模拟软件,它可以很快捷的模拟网络中的各种设备(交换机、路由器、台式电脑、笔记本电脑、服务器、网络云),搭建各种网络环境,模拟网络拓扑结构等。下面天
s4中单影流之主劫天赋加点图+玩法 火男中单s6天赋加点图
s4中单影流之主劫天赋加点图+玩法——简介本篇介绍S4影流之主劫天赋加点图和玩法。s4中单影流之主劫天赋加点图+玩法——工具/原料lols4中单影流之主劫天赋加点图+玩法——天赋s4中单影流之主劫天赋加点图+玩法 1、攻击天赋:4点巫
S5诺克萨斯之手上单天赋加点图_S5德莱厄斯天赋 德莱厄斯上单天赋
S5诺克萨斯之手上单天赋加点图_S5德莱厄斯天赋——简介诺克是一个操作比较简单的英雄,很多人都喜欢玩,大招劈人的快感,让人很是喜欢。今天为大家介绍一下诺克上单的天赋!S5诺克萨斯之手上单天赋加点图_S5德莱厄斯天赋——工具/原料英雄
DNF传说之灵符怎么用 精 大转移后传说之灵符
DNF传说之灵符怎么用 精——简介 DNF是网络竞技游戏《地下城与勇士》的英文简称,《地下城与勇士》是一款韩国网络游戏公司NEOPLE开发的免费角色扮演2D游戏,由三星电子发行,并于2005年8月在韩国正式发布。该游戏是一款2D卷轴式横版格斗