购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

4.4 面向对象分析

综观计算机软件发展史,许多新方法和新技术都是在编程领域首先兴起,进而发展到软件生命周期的前期阶段——分析与设计阶段。结构化方法经历了从“结构化编程”、“结构化设计”到“结构化分析”的发展历程,面向对象的方法也经历了从“面向对象的编程”(Object-Oriented Programming,OOP)、“面向对象的设计”(Object-Oriented Design,OOD)到“面向对象的分析”(Object-Oriented Analysis,OOA)的发展历程。1989年之后,面向对象方法的研究重点开始转向软件生命周期的分析阶段,并将OOA和OOD密切地联系在一起,出现了一大批面向对象的分析与设计(OOA&D)方法。到目前为止,公开发表并具有一定影响的OOA&D方法已达数十种。

由于各种OOA方法所强调的重点与该方法的主要特色不同,因此所产生的OOA模型从整体形态、结构框架到具体内容都有较大的差异。

4.4.1 OMT方法简介

1991年,James Rumbaugh在《面向对象的建模与设计》( Object-Oriented Modeling and Design )一书中提出了面向对象分析与设计的OMT(Object Modeling Technique)方法。20世纪90年代中期,笔者曾使用OMT方法开发了“印典”、“书林”等排版系统。本书的OOA模型主要依据OMT方法,同时参考了Peter Coad和Edward Yourdon的OOA模型。

OMT方法的OOA模型包括对象模型、动态模型和功能模型。

OMT方法的三个模型,分别从三个不同侧面描述了所要开发的系统:功能模型指明了系统应该“做什么”;动态模型明确了什么时候做(即在何种状态下接受了什么事件的触发);对象模型则定义了做事情的实体。这三种模型相互补充、相互配合,三者之间具有下述关系。

(1)动态模型展示了对象模型中每个对象的状态及它接受事件和改变状态时所执的操作;而功能模型中的处理则对应于对象模型中的对象所提供的服务。

(2)对象模型展示了动态模型中是谁改变了状态和经受了操作;而功能模型中的处理则可能产生动态模型中的事件。

(3)对象模型展示了功能模型中的动作者、数据存储和流的结构;而动态模型则展示了功能模型中执行加工的顺序。

1.建立对象模型

Peter Coad和Edward Yourdon在1991年出版的《面向对象的分析》( Object-Oriented Analysis )一书中指出,复杂系统的对象模型通常由下述五个层次组成:类及对象层、结构层、主题层、属性层和服务层。上述五个层次对应着建立对象模型的五项主要活动:确定类与对象、确定结构与关联、划分主题、定义属性和定义服务。但这五项活动完全没必要顺序完成,也无需彻底完成一项活动之后再开始另外一项活动。

(1)确定类与对象。 类与对象是在问题域中客观存在的,系统分析的重要任务之一就是找出这些类与对象。首先找出所有候选的类与对象,然后进行反复筛选,删除不正确或不必要的类与对象。

(2)确定结构与关联。 结构与关联反应了对象(或类)之间的关系,主要有以下几种。

(3)划分主题。 在开发大型、复杂软件系统的过程中,为了降低复杂程度,需要把系统划分成几个不同的主题。注意,应该按问题域而不是用功能分解方法来确定主题,应该按照使不同主题内的对象相互间依赖和交互最少的原则来确定主题。

(4)定义属性。 为了发现对象的属性,首先考虑借鉴以往的OOA结果,看看相同或相似的问题域是否有已开发的OOA模型,尽可能复用其中同类对象的属性定义。然后,按照问题域的实际情况,以系统责任为目标进行正确的抽象,从而找出每一对象应有的属性。

(5)定义服务。 发现和定义对象的服务,也应借鉴以往同类系统的OOA结果并尽可能加以复用。然后,研究问题域和系统责任以明确各个对象应该设立哪些服务,以及如何定义这些服务。

2.建立动态模型

建立动态模型的第一步,是编写典型交互行为的脚本。虽然脚本中不可能包括每个偶然事件,但至少必须保证不遗漏常见的交互行为。第二步,从脚本中提取出事件,确定触发每个事件的动作对象及接受事件的目标对象。第三步,排列事件发生的次序,确定每个对象可能有的状态及状态间的转换关系,并用状态图描绘它们。最后,比较各个对象的状态图,检查它们之间的一致性,确保事件之间的匹配。

3.建立功能模型

OMT方法中的功能模型实际上就是结构化方法中的数据流图。从这点看,OMT方法并不是“纯”面向对象的。这是OMT方法的一大缺陷。

1992年,Ivar Jacobson在《面向对象的软件工程——用例驱动的途径》( Object-Oriented Software Engineering A Use Case Driven Approach )中首次提出了“用例”(use case)的概念。随后,有人提出以用例图取代数据流图进行需求分析和建立功能模型,这应该被看做是对OMT方法的重大改进。使用用例图建立起来的系统模型也被称为用例模型。

4.4.2 用UML进行分析

首先,我们结合图4-17来领会一下OO的世界观。

在结构化的理论基础下,我们会将应用分解成为一个个功能模块、子功能模块、功能接口等,它完全与现实问题域的东西没有具体的联系。从图4-17可以看出,使用OO的思想所建立的系统模型就是对问题域的完整的、直接的映射。也就是从现实世界中抽象出一个模型,然后在计算机中实现出来。

图4-17 OO世界观

也就是说,从面向对象的角度来看,世界是由对象组成的。由于任何给出的业务功能都是由一整套协作的对象所支持实现的。因此,采用面向对象分析方法时,我们需要识别出与系统相关的对象,并且描述这些对象的属性,以及它们之间的关系;另一方面,我们还需要了解这些对象之间是如何协作,从而完成系统功能的。

综上所述,采用面向对象分析方法,整个分析阶段通常包括以下两个工作任务:建立一个反映问题域静态关系的概念模型,通常使用类图来表示;建立一个反应系统行为的动态模型,即用例模型。

1.建立域模型

“问题域”是指一个包含现实世界事物与概念的领域,这些事物和概念与所设计的系统要解决的问题有关。而建立概念模型,又称为问题域建模或域建模,也就是找到代表那些事物与概念的“对象”。

(1)寻找类。 寻找类的方法有很多种,其中最广泛应用的莫过于“名词动词法”,也就是阅读需求文档,找出名词和名词短语,从中提取对象与属性,通常带有所有格的名词是属性而非对象。找出动词与动词短语,从中提取操作与关联。

第一步:列出所有的备选类。即将需求中所有的名词和名词短语列举出来。

第二步:决定候选类。很显然,不是所有的名词和名词短语都是系统中的一个合适的候选类,因为有的在系统之外,有的与系统不相关,有的名词概念较小,只适合于作为某个候选类的属性。因此,我们必须对其进行筛选,将不合适的滤掉。

不过,在采用名词动词法寻找类的时候,有些团队会陷入一个误区,那就是花费过多的时间,甚至到了“咬文嚼字”的地步,这样会使分析迷失方向。

(2)确定类之间的关联。 当我们完成了类的寻找工作之后,需要理清这些类之间的层次关系,如关联、继承、聚合等,然后通过UML的类图工具将这些关系记录下来。如图4-18所示是一个与个人藏书管理系统相关的领域模型。

当完成了这些关联关系的建模之后,需要细化这些关系的描述。例如,我们从图4-18中就会产生一些疑问,如一本书可以有几条借阅记录等。这就需要将这些关系之间的多重性进行一些描述,当然这些描述必须是来源于业务规则,与领域相关的,如果还不清晰的可以暂时放在一边。如图4-19所示是一个修改过后的模型。

图4-18 域模型示例(1)

图4-19 域模型示例(2)

通过关联关系的识别与建模,我们可以对问题域中的各个类之间层次结构关系、协作关系有一个相对完整的认识与理解。

(3)为类添加职责。 在前面两步中,我们找到了与问题域本质相关的主要概念类,而且还理清了它们之间的协作关系,此后就应该为这些类添加其相应的主要职责。什么是类的职责呢?类的职责包括以下两方面的内容:

不过要注意的是,为类添加职责与找到类之间的关联关系一样,这个阶段也只是找到那些主要的、明显的、与业务规则相关的部分。切忌在这个阶段不断地细化,甚至引入一些与具体实现相关的技术内容,如数据库、分布式对象之类的东西。

你可以通过CRC技术来发现这些类的职责,然后在原来的类模型的基础上进行完善,图4-20是一个完成了主要类职责之后的概念模型。

(4)域模型的详细度。 在前面,我们一再强调,在做域模型时要适度,那么到底应该详细到什么程度呢?有些关于OO的书中建议只列出类,以及类之间的主要关联关系,不要对关联关系进行描述,更不要体现类的职责;而有些OO的书则认为应该将这些东西都列出来。其实干巴巴地讨论这个问题是没有任何意义的,根据笔者长期的实践,总结了以下两点。

图4-20 完整的域模型示例

总之,模型不是我们要生产的目标产物,而是过程中的一个辅助工作,只要能够利用其帮助团队更好的开发,那就是详细也罢、简约也罢,都是好模型。

2.建立用例模型

有些制作精细的“模型车”不管从外观还是内部结构上都与真车一模一样,但是却不能够像真车那样行驶,缺了什么呢?缺的是每个零件只是“神似”,而非“真是”,换一句话说就是处于静态状态下是相像的,但是无法动起来,无法实现这些零件本该实现的功能,这就使得模型车无法真正地开起来。

当我们完成了概念模型的建立时,仅仅是打造完成了一辆“模型车”而已,只能够帮助开发团队更好地理解系统所涉及的问题领域,帮助对要开发系统相关的业务知识建立正确、完整、清晰的理解。但还无法开始构建系统。要想让“模型车”开起来,最重要的就是建立反映系统行为的动态模型,也就是用例模型。

(1)用例是什么? Ivar Jacobson是这样描述的:“用例实例是在系统中执行的一系列动作,这些动作将生成特定参与者可见的价值结果。一个用例定义一组用例实例。”

首先,我们从定义中得知用例是由一组用例实例组成的,用例实例也就是常说的“使用场景”,是用户使用系统的一个实际的、特定的场景。其次,我们可以知道,用例应该给参与者带来可见的价值,这点很关键。最后,用例是在系统中的。

(2)用例模型是如何产生的? 用例技术自从诞生起,就被广为关注,现在已经成为现代软件工程公认的最佳需求分析技术之一。近几年来,在国内的开发团队中也开始逐渐被接受,不过由于国内认识用例是从UML开始的,因此许多人误把用例图当作用例模型。另外,还有许多初用用例分析的开发组织,误将其当作一种分解技术,致使做出来的分析与功能分解酷似,以致失去了用例分析技术带来的益处。

其实,用例分析技术是一种需求合成技术,它的合成过程如图4-21所示。

图4-21 用例建模过程示意图

用例分析技术也就是采用现有的需求捕获技术从客户、原有系统、文档中找到需求,记录下来,然后从这个零散的要求、特性中进行整理、提炼,从而建立用例模型。千万记住,不要尝试向你的客户询问诸如“你还有什么用例吗?”之类的问题。

(3)识别参与者。 参与者(Actor)是同系统交互的所有事物,该角色不仅可以由人承担,还可以是其他系统、硬件设备、甚至是时钟。

要注意的是,参与者一定在系统之外,不是系统的一部分。我们可以通过谁使用这个系统?谁安装这个系统?谁启动这个系统?谁维护这个系统?谁关闭这个系统?哪些其他的系统使用这个系统?谁从这个系统获取信息?谁为这个系统提供信息?是否有事情自动在预计的时间发生?等一系列问题来帮助发现系统的参与者。

(4)合并需求获得用例。 将参与者都找到之后,接下来就是仔细地检查参与者,为每一个参与者确定用例。而其中的依据主要来源于已经获取得到的“特征表”。

合并后,将产生用例,而用例的命名应该注意采用“动词(短语)+名词(短语)”的形式,而且最好能够对其进行编号,这也是实现跟踪管理的重要技巧,通过编号可以将用户的需求落实到特定的用例中去。

(5)绘制成用例图。 最后将识别到的参与者,以及合并生成的用例通过用例图的形式整理出来,以获得用例模型的框架,也算是得到一个中间的成果,如图4-22所示。

图4-22 用例图示例

千万不要以为到此用例分析就结束了。这仅仅是一个好的开端,接下来的工作才是最重要的一环,也是用例发挥作用的关键。

(6)细化用例描述。 用例的描述可以迭代完成,先对一些重要的用例编制相对细致的用例描述,对于一些不重要的,可以留待以后再补充完成。

用例描述通过包括以下几个部分完成。

①用例名称:应该与用例图相符,并写上其相应的编号;

②简要说明:对该用例对参与者所传递的价值结果进行描述,应注意语言简要,使用用户能够阅读的自然语言。

③事件流:也就是该用例所完成的工作步骤,在编写时应该注意以下几点。

另外,事件流的编写过程也是可以分阶段、迭代进行的,对于优先级高的用例花更多的时间进行细化;对优先级低的用例可以先简略地将主要事件流描述清楚。另外,对于一些事件流较为复杂的,可以在用例描述中引用顺序图、状态图、协作图等手段进行描述。

④非功能要求:主要对该用例所涉及的非功能性需求进行描述。由于其通常很难在事件流中进行表述,因此单列为一小节进行阐述。这些需求通过包括法律法规、应用程序标准、质量属性(可用性、可靠性、性能、支持性等)、兼容性、可移植性,以及设计约束等方面的需求。在这些需求的描述方面,一定要注意使其可度量、可验证,否则就容易流于形式,形同摆设。

⑤前置条件:是执行用例之前必须存在的系统状态,这部分内容如果在当前不容易确定可以在后面再细化。

⑥后置条件:用例执行完毕系统可能处于的一组状态,这部分内容如果在当时不容易确定也可以在后面再细化。

⑦扩展点:如果包括扩展或包含用例,则写出扩展或包含用例名,并说明在什么情况下使用。在本例中,由于用例图里没有相应的内容,因此可以直接写无。如果有,则应该在编写事件流的同时进行编写。

⑧优先级:说明用户对该用例的期望值,可以为以后开发时制订先后顺序。可以采用满意度/不满意度指标进行说明,其中满意度的值为0~5,指如果实现该功能,用户的满意程度;而不满意度的值也为0~5,是指如果不实现该功能,用户的不满意程度。

下面是图4-22中所对应的其中一个较重要的用例“新增书籍信息”的用例描述,这里给出的是一个相对完整的版本,这些内容不一定要一次完成。 YJLHe0V5qcRD3NQagkn2HptXd34/l8vIqHmDWgeupbLjBjPGvAtdWRycWfgm2EGi

点击中间区域
呼出菜单
上一章
目录
下一章
×