第3章 软件体系结构建模和UML 构建一个软件系统最困难的部分是确定构建什么。其他部分的工作不会像这部分工作一样,在出错之后会如此严重地影响随后实现的系统,并且在以后修补会如此的困难。 ——Frederick P.Brooks.Jr. 软件体系结构是一系列相关的抽象模式,用于指导大型软件系统各个方面的设计。软件体系架构描述的对象是直接构成系统的抽象组件,各个组件之间的连接则明确地和相对细致地描述组件之间的通信。软件体系结构建模体现了软件设计的思想,在系统需求和系统实现之间架起了一座桥梁。软件工程师按照设计人员建立的模型开发出符合设计目标的软件系统,而且软件的维护和改进也基于软件体系结构模型。 统一建模语言(Unified Modeling Language,UML)是一种为面向对象系统的产品进行说明、可视化、编制文档的一种标准语言,是非专利的第三代建模和规约语言。 UML使用面向对象设计的建模工具,独立于任何具体程序设计语言。同时,UML具有广泛的建模能力,在消化、吸收、提炼现有的软件建模语言的基础上提出,集百家之长,是软件建模语言的集大成者。UML突破了软件的限制,吸收了其他领域的建模方法,并根据建模的一般原理,结合了软件的特点,具有坚实的理论基础和广泛性。 本章共分七个部分: 3.1节为软件体系结构建模概述,3.2节为基于软件体系结构的开发,3.3节为UML概述,3.4节为面向对象概述,3.5节为UML中的结构建模,3.6节为UML中的行为建模,3.7节为UML工具。 3.1软件体系结构建模概述 研究软件体系结构的首要问题是如何表示软件体系结构,如何对软件体系结构建模。 根据建模的侧重点不同,可以将软件体系结构的模型分为五种,即结构模型、框架模型、动态模型、过程模型和功能模型。五个模型中,最常用的是结构模型和动态模型。 (1) 结构模型: 是一个最直观、最普遍的建模方法。这种方法以体系结构的构件、连接件、其他概念来刻画结构,并力图通过结构来反映系统的重要语义内容,包括系统的配置、约束、隐含的假设条件、风格、性质。研究结构模型的核心是体系结构描述语言。 (2) 框架模型: 与结构模型类似,但不太侧重描述结构的细节,而更侧重于整体的结构。框架模型主要以一些特殊的问题为目标,建立只针对和适应该问题的结构。 (3) 动态模型: 是对结构或框架模型的补充,研究系统的大粒度行为性质。例如,描述系统的重新配置或进化。动态可能指系统总体结构的配置、建立、拆除通信通道或计算的过程。 (4) 过程模型: 研究构造系统的步骤和过程。因而,其结构是遵循某些过程脚本的结果。 (5) 功能模型: 该模型认为,体系结构是由一组功能构件按层次组成由下层向上层提供服务,可以看作是一种特殊的框架模型。 上述五种模型各有所长,也许将五种模型有机地统一在一起形成一个完整的模型来刻画软件体系结构更合适。例如,英属哥伦比亚大学(UBC)的Philippe Kruchten在1995年提出了一个“4+1”的视角模型。“4+1”模型从五个不同的视角(包括逻辑视角、过程视角、物理视角、开发视角、场景视角)来描述软件体系结构(具体内容参见4.2节)。 3.2基于软件体系结构的开发 良好的体系结构可以为软件开发和维护带来好处,主要体现在以下几个方面。 (1) 经过50多年的软件开发实践,现在很少待开发的软件系统与以前的系统没有任何相似之处,识别相似系统的通用结构模式,有助于理解系统之间的高层联系,使得新系统可以作为以前系统的变种来构造。 (2) 合适的体系结构是软件系统成功的关键,而不合适的体系结构往往导致灾难性的后果。 (3) 对软件体系结构的准确理解,可以使开发人员在不同的设计方案中做出理性的选择。 (4) 体系结构对于分析和描述复杂系统的高层属性是十分必要的。 (5) 各种体系结构风格的提炼、描述及普遍采用,可以丰富设计人员的“词汇”,便于在系统设计中互相交流。 (6) 目前,相当大的维护工作量花费在程序理解方面,如果在软件开发文档中清楚地记录了系统的体系结构,不但可以显著地节省软件理解的工作量,而且便于在软件维护全过程中保持系统的总体结构和特性不变。 由此可见,体系结构在整个软件生存周期中扮演着重要的角色,其软件开发过程包括以下几个主要活动。 (1) 通过对特定领域应用软件进行分析,提炼其中的稳定需求和易变需求,建立可复用的领域模型。根据用户需求和领域模型,产生应用系统的需求规格说明。 (2) 在领域模型的基础上,提炼面向特定领域的软件体系结构。高层设计的任务是根据需求规格说明进行体系结构设计,通过复用体系结构库中存放的面向特定领域的体系结构,或创造适合该应用环境的体系结构,并加以提炼入库,以备将来的复用。在体系结构的框架指导下,把系统功能分解到相应的构件和连接件。构件和连接件往往不是简单的模块或对象,甚至还可能包含复杂的结构,因此可能需要多层次的体系结构设计,直至构件和连接件可以被设计模式或单个的对象处理为止。 (3) 低层设计主要解决具体构件和连接件的设计问题,通过复用设计件库中存放的设计模式、对象、其他类型的可复用设计件,或根据情况设计新的构件,并提炼入库。低层设计的结果可以直接编程实现。 体系结构从宏观上描述系统的总体结构,设计模式和对象从微观上解决实际的设计问题,分别对应于高层设计和低层设计分析需求规格说明书,获取系统的功能性需求和非功能性需求,需要确定系统的边界,识别出所有的参与者和用例,功能性需求和非功能性需求使用用例和场景来进行描述。需要将具有相同或相似属性和方法的一类对象抽象为一个类,比较各个类的属性和方法,确定不同的类之间的关系。根据类之间的关系生成类图,将密切相关的类划分为一组形成构件,根据构件端口确定构件之间的关联关系,根据功能性需求和非功能性需求确定系统应该采用的体系结构风格。 在体系结构设计方案中,若存在相同或相似的解决方案,将直接进行复用,或经过简单修改之后再复用; 如果没有,则需要进行重新设计,体系结构设计师、系统分析人员和客户以及相关的技术实现人员对体系结构设计结果进行评审,确定所提出的解决方案是否能够满足用户的要求,是否能够提高资源的复用效率。 3.3UML概述 3.3.1UML的发展历程 20世纪60年代初,MIT的一些学者开始使用“对象”概念。但是,公认的面向对象的思想起源于Simula 67语言。 从1989年到1994年,面向对象建模语言数量从不到10种增加到了50多种。在众多的建模语言中,语言的创造者努力推崇自己的产品,并在实践中不断完善。但是,面向对象方法(ObjectOriented Method)的用户并不了解不同建模语言的优缺点及相互之间的差异,因而很难根据应用特点选择合适的建模语言,于是爆发了一场方法大战。20世纪90年代中期,一批新方法出现了,其中最引人注目的是Booch1993、OOSE、OMT2等。 UML的三位大师分别是Booch、Rumbaugh和Jacobson,如图31所示。UML是一种商业决策,由Booch带头,把Rumbaugh和Jacobson提出的有用内容放在一起。 图31UML和Booch、Rumbaugh、Jacobson Booch是面向对象方法最早的倡导者之一,提出了面向对象软件工程的概念。1991年,他将以前面向Ada的工作扩展到整个面向对象设计领域。接着,Rumbaugh提出了面向对象的建模技术方法,采用了面向对象的概念,并引入各种独立于语言的表示符。这种方法用对象模型、动态模型、功能模型、用例模型共同完成对整个系统的建模,所定义的概念和符号可用于软件开发的分析、设计、实现的全过程,软件开发人员不必在开发过程的不同阶段进行概念和符号的转换。随后,Jacobson于1994年提出了OOSE方法,其最大特点是面向用例,并在用例的描述中引入了外部角色的概念。OOSE比较适合支持商业工程和需求分析。Coad/Yourdon方法,即著名的OOA/OOD,是最早的面向对象的分析和设计方法之一。该方法简单、易学,适合于面向对象技术的初学者使用,但由于该方法在处理能力方面的局限,目前很少使用。 1994年10月,Booch和Rumbaugh首先将Booch 1993和OMT2统一起来,并于1995年10月发布了第一个公开版本,称为统一方法UM 0.8(Unitied Method)。1995年秋,Jacobson加盟到这一工作中。经过三人的共同努力,于1996年6月和10月分别发布了两个新的版本,即UML 0.9和UML 0.91,并将UM重新命名为统一建模语言(Unified Modeling Language,UML)。 1996年,一些机构将UML作为其商业策略已日趋明显。UML的开发者得到了来自公众的正面反应,并倡议成立了UML成员协会,以完善、加强、促进UML的定义工作。这一机构对UML 1.0及UML 1.1的定义和发布起到了重要的促进作用。2001年,推出了UML 2.0新的业界标准。当时UML共收集了20种模型,每种模型互相引用。当前版本的统一建模语言是在2017年12月公布的UML 2.5.1。 总之,UML是一种定义良好、易于表达、功能强大、普遍适用的建模语言。作为大众所接受的标准建模语言,UML融入了软件工程领域的新思想、新方法、新技术。UML的作用域不但支持面向对象的分析与设计,而且支持从需求分析开始的软件开发的全过程。UML被广泛接受的一个关键原因是它的图形化,因为图容易被大家接受。 面向对象事实上是反对瀑布模型的。早期的软件需求工程或结构化分析是受当时的编程语言和瀑布模型的影响而发展起来的,而UML主要受面向对象的影响。 3.3.2UML的特点 UML的主要特点可以归结为以下几点。 (1) 统一标准。UML统一了Booch、OMT、OOSE等方法中的基本概念,已成为OMG的正式标准,提供了标准的面向对象的模型元素的定义和表示。 (2) 面向对象的特性。UML还吸取了面向对象技术领域中其他流派的长处,其中也包括非面向对象方法的影响。UML符号表示考虑了各种方法的图形表示,删掉了大量易引起混乱的、多余的、极少使用的符号,添加了一些新符号。因此,在UML中汇入了面向对象领域中很多人的思想。这些思想并不是UML的开发者们发明的,而是开发者们依据最优秀的面向对象方法和丰富的计算机科学实践经验综合提炼而成的。 (3) UML在演变过程中还提出了一些新的概念。在UML标准中,新加了模板(Stereotypes)、职责(Responsibilities)、扩展机制(Extensibility mechanisms)、线程(Threads)、过程(Processes)、分布式(Distribution)、并发(Concurrency)、模式(Patterns)、合作(Collaborations)、活动图(Activity diagram)等新概念,并清晰地区分类型(Type)、类(Class)和实例(Instance)、细化(Refinement)、接口(Interfaces)和组件(Components)等概念。 (4) 立于过程。UML作为建模语言,不依赖特定的程序设计,独立于开发过程。 (5) UML对系统的逻辑模型和实现模型都能清晰地表示,可以用于复杂软件系统的建模。 可以认为,UML是一种先进实用的标准建模语言,但其中某些概念尚待实践来验证,UML也必然存在一个进化过程。 3.3.3UML 2.5分类 当前版本的统一建模语言是UML 2.5.1,在2017年12月公布的“UML 2.5.1规格”中,修正了一些UML 2.5的小问题。UML 2.5简化、重组了UML规范文档,不再有两格独立的Infrastructure和Specification文档,UML 2.5规范是单个文档。UML 2.5可以按照图32进行分层次分类。其中,灰色虚线标记的不是官方UML 2.5分类图的一部分。同时,表31所示给出了UML 2.5的14种图术语和解释。 图32UML 2.5的分类图 表31UML 2.5的14种图术语和解释 序号UML图简 要 说 明 1用例图(UseCase Diagram)从用户的角度提供系统或业务流程功能的概述,用户“使用”系统的方式是创建用例图的起点 2活动图(Activity Diagram)对系统中任何位置的流程进行建模,特别是描述正常用户交互以及替代和例外的用例中的流程,由这些活动图很好地建模 3类图(Class Diagram)表示类、定义和关系,问题空间中的类和实体也是解决方案空间中的详细技术实体。定义类的属性和操作包含在此类图中。类图中的关系说明了类如何与其他类交互、协作、继承。类还可以表示关系表、用户界面和控制器 4序列图(Sequence Diagram)根据对象的时间轴模拟对象之间的交互,对象可以在这些图上具体显示,也可以是属于类的匿名对象,运行时对象之间的消息执行顺序由这些图很好地建模 5交互概述图(Interaction Diagram)以一般的高级别呈现系统内交互的概述,使得能够理解UML图(如序列图)如何依赖于彼此并且彼此相关 6通信图(Communication Diagram)显示对象在运行时如何在内存中相互通信(交互),这些通信图在其目的方面类似于序列图,但是它们的代表性是不同的 7对象图(Object Diagram)在运行时显示内存中的对象及其链接,因此,这些对象图还有助于在实践中可视化多重性 8状态机图(State Machine Diagram)显示内存中对象的运行时生命周期,这样的生命周期包括对象的所有状态以及状态改变的条件 9组合结构图(Composite Structure Diagram)组合结构图在运行时模拟组件或对象行为,显示系统执行期间组件的布局、关系、实例 10组件图(Component Diagram)从结构上模拟组件及其关系,这些组件可以包括例如可执行文件、可链接库、Web服务、移动服务,这些图表为系统的架构决策增加了价值 11部署图(Deployment Diagram)对系统的硬件节点和处理器的体系结构进行建模,并提供显示软件组件所在节点的机会 12包图(Package Diagram)表示系统组织的子系统和区域,还可以模拟包之间的依赖关系,帮助将业务实体与用户界面、数据库、安全性、管理包分开 13时序图(Timing Diagram)模拟时间的概念以及对象状态随时间变化的方式,此外,这些图可以同时比较多个对象的状态 14配置文件图(Profile Diagram)允许创建可扩展的配置文件,这些配置文件可应用于从配置文件继承的元素,这些图表通过以受控方式扩展标准来增加价值 UML是把有用的、各种各样的模型都放进去,既有数据流图,也有状态图,还有类图、活动图,等等。所以,UML的缺陷在于用的模型实在太多、太复杂,而且模型之间不能自动转换。虽然图画得很漂亮,但是都要手工或机器辅助完成。 2000年左右,出现了另一种声音,说UML太复杂,模型与编程不同步。按照UML的要求,首先要做一个模型,做完之后再编程。如果程序要修改,要按照各式各样软件工程的要求,先改模型再改软件。但是,一般人没有时间这么做,总有人或者只改了程序,或者只改了模型,这样模型与程序是风马牛不相及的,模型用完之后就被扔掉了。微软的Keith Short说UML的好处是,首先可以当“白板”供讨论时使用,然后可以当“手纸”,写在手纸上的东西用完就扔掉。 3.4面向对象概述 从事软件开发的工程师常常有这样的体会: 在软件开发过程中,使用者会不断地提出各种更改要求,即使在软件投入使用后,也常常需要对其做出修改,在用结构化开发的程序中,这种修改往往是很困难的,而且还会因为计划或考虑不周,不但旧错误没有得到彻底改正,又引入了新的错误。另一方面,在过去的程序开发中,代码的重用率很低,使得程序员的效率并不高,为提高软件系统的稳定性、可修改性、可重用性,人们在实践中逐渐创造出软件工程的一种新途径——面向对象方法学。目前,面向对象开发方法已日趋成熟。 面向对象开发方法有Coad方法、Booch方法、OMT方法等。UML不但统一了Booch方法、OMT方法、OOSE方法的表示方法,而且对其做了进一步的发展,最终统一为大众接受的标准建模语言。UML是一种定义良好、易于表达、功能强大且普遍适用的建模语言,融入了软件工程领域的新思想、新方法和新技术。 面向对象方法是当今主流的软件开发方法,其基础在于将客观世界中的应用问题看成是由实体及其相互关系组成的,将与某一应用问题有关的实体抽象为问题空间的对象。面向对象开发以系统化的方法学进行指导,其中包含了各种概念、技术、过程。面向对象方法学的出发点和基本原则是尽可能模拟人类习惯的思维方式,使开发软件的方法与过程尽可能接近人类认识世界、解决问题的方法与过程。 由于客观世界的问题都是由客观世界中的实体及实体相互间的关系构成的,因此,我们把客观世界中的实体抽象为对象(Object)。持面向对象观点的程序员认为,计算机程序的结构应该与所要解决的问题一致,而不是与某种分析或开发方法保持一致。所以,“面向对象”是一种认识客观世界的世界观,是从结构组织角度模拟客观世界的一种方法。一般人们在认识和了解客观现实世界时,通常运用以下构造法则。 (1) 区分对象及其属性,例如,区分具体的一辆汽车和它的重量、最大速度。 (2) 区分整体对象及其组成部分,例如,区分台式计算机的组成(主机、显示器等)。 (3) 不同对象类的形成以及区分,例如,所有类型的计算机(大、中、小型计算机、服务器、工作站和普通微型计算机等)。 可以看出,面向对象所带来的好处是程序的稳定性与可修改性(由于把客观世界分解成一个一个的对象,并且把数据和操作都封装在对象的内部)、可复用性(通过面向对象技术,我们不但可以复用代码,而且可以复用需求分析、设计、用户界面,等等)。 3.4.1基本概念 1. 对象 对象(Object)指的是一个独立的、异步的、并发的实体,能“知道一些事情”(即存储数据),“做一些工作”(即封装服务),并“与其他对象协同工作”(通过交换消息),从而完成系统的所有功能。因为所要解决的问题具有特殊性,所以对象是不固定的。对象是现实世界中的个体或事物的抽象表示,是其属性和相关操作的封装。 属性表示对象的性质,属性值规定了对象所有可能的状态。对象的操作是指该对象可以展现的外部服务。对象是事物的本质,是不会随周围环境改变而变化的相对固定的最小的集合。 对象实现了数据和操作的结合,使数据和操作封装于对象的统一体中。 例如,在计算机屏幕上画多边形,每个多边形是一个用有序顶点的集所定义的对象。这些顶点的次序决定了它们的连接方式,顶点集定义了一个多边形对象的状态,包括它的形状和它在屏幕上的位置,在多边形上的操作包括: draw(屏幕显示)、move(移动)、contains(检查某点是否在多边形内)。 2. 类 类(Class)的定义包括一组数据属性和在数据上的一组合法的操作。在一个类中,每个对象都是类的实例(instance),类的对象具有相同的方法集,有相同或相似性质的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象。类具有属性,是对象的状态的抽象,用数据结构来描述类的属性; 类具有操作,是对象的行为的抽象,用操作名和实现该操作的方法来描述。 3. 继承性 广义地说,继承(Inheritance)是指能够直接获得已有的性质和特性,而不必重复定义它们。在面向对象的软件技术中,继承是子类自动地共享基类中定义的数据和方法的机制。一个类直接继承其父类的全部描述(数据和操作)。继承具有传递性,继承性使得相似的对象可以共享程序代码和数据结构,从而大大减少了程序中的冗余信息。使得对软件的修改变得比过去容易得多。 继承性使得用户在开发新的应用系统时,不必完全从零开始,可以继承原有的相似系统的功能,或者从类库中选取需要的类,再派生出新的类以实现所需要的功能,所以,继承的机制主要是支持程序的重用和保持接口的一致性。 父类是高层次的类,表达共性; 子类是低层次的类,表达个性。子类通过继承机制获得父类的属性和操作。 例如,电视机、电话、计算机等都是电子产品,具有电子产品的公共特性,当定义电视机类Video,电话类Telephone和计算机类Computer时,为避免公共特性的重复编码,可将这些电子产品的公共特性部分定义为电子产品类,将Video、Telephone和Computer定义为它的子类,子类继承了父类的所有属性和操作,而且子类自己还可扩充定义自己的属性和操作,如电子产品类具有型号、价格、颜色等属性,Computer则继承了这些属性,并扩充自己的显示类型、内存大小等属性。又如汽车是轿车、吉普车及卡车的父类,轿车、吉普车及卡车是汽车的子类。 父类和子类是相对的,父类之上可有另一个父类,而成为其子类。 4. 多态性 在面向对象的软件技术中,多态性(Polymorphism)是指子类对象可以像父类对象那样使用,同样的消息既可以发送给父类对象也可以发送给子类对象。也就是说,在类等级的不同层次中,可以共享(公用)一个行为(方法)的名字,然而,不同层次中的每个类却各自按自己的需要来实现这个行为。当对象接收到发送给它的消息时,根据该对象所属的类动态选用在该类中定义的实现算法。 5. 重载 在面向对象的软件技术中有两种重载(Overloading),其中,函数重载是指在同一作用域内的若干个参数特征不同的函数可以使用相同的函数名字; 运算符重载是指同一个运算符可以施加于不同类型的操作数上面。当然,当参数特征不同或被操作数的类型不同时,实现函数的算法或运算符的语义是不相同的。重载进一步提高了面向对象系统的灵活性、可读性。 6. 消息 在面向对象领域,两个对象的交互是通过消息(Message)的发送和接收来完成的,消息分为以下几种。 (1) 简单消息: 只是表示控制如何从一个对象发给另一个对象,并不包含控制的细节。 (2) 同步消息: 意味着阻塞、等待,如果对象A给对象B发送一个消息,对象A会等待对象B执行完这个消息,接着才进行自身的工作。 (3) 异步消息: 意味着非阻塞,如果对象A给对象B发送一个消息,对象A不必等待对象B执行完这个消息,就可以接着进行自身的工作。 消息传递是对象与其外界时间相互关联的唯一途径。对象之间进行通信的结构称为消息。在对象的操作中,当一个消息发送给某个对象时,消息包含接收对象去执行某种操作的信息。发送一条消息,至少要包括说明接受消息的对象名、发送给该对象的消息名(即对象名、方法名)。 7. 聚集 在客观世界中有若干类,聚集(Aggregation)表示类之间的关系是整体与部分的关系,通常有以下两种主要的结构关系。 (1) 一般具体结构: 称为分类结构,也可以说是“或”关系,或者是“is a”关系。 (2) 整体部分结构: 称为组装结构,之间的关系是一种“与”关系,或者是“has a”关系。 面向对象开发方法的核心是利用面向对象的概念、方法对软件进行需求分析和设计,建立面向对象的软件分析和设计模型。 3.4.2面向对象方法的优势 相对于传统的结构化方法和面向数据的方法,面向对象方法拥有以下优势。 1. 支持软件的重用性 重用性是指同一事物不经过修改或稍加修改就可以多次重复使用的性质。软件重用是软件工程追求的目标之一。在面向对象方法被广泛应用以前,基于结构化方法的软件复用几乎没有取得有意义的进展。在面向对象方法中,情形发生了变化。在源代码级复用方面,面向对象方法通过继承和接口等机制,使得复用者不需要直接修改被复用的类。在设计级复用方面,迅速发展的设计模式技术在软件业大显身手,现在,全球范围内实施的软件项目,大多离不开面向对象的重用技术的支持,如Java库、J2EE框架、.NET 的FrameWork 4.0框架。 2. 提高软件可维护性和安全性 面向对象方法通过对属性和操作的封装,实现了软件工程倡导的信息隐藏的原则。面向对象的软件设计中,每个类拥有完成其操作所必需的数据,这些数据通过访问权限控制关键字private隐藏于类的内部,或者通过protected关键字隐藏于类及子类的内部,外界对类的内部数据的访问、修改只能通过该类对外公开的接口函数来实现,这样,安全性就有了保障。相对于传统的结构化方法,面向对象方法更容易造就高质量的软件结构。 为了发挥面向对象开发方法所具备的优势,就必须采用面向对象的思维方式来设计面向对象软件,避免采用结构化思维方式和工具来开发面向对象软件。UML就是为此目标而出现的,UML通过提供多种视图模型,使开发人员能够采用面向对象方法对软件进行全面分析和设计,提高软件开发的效率和质量。 3.5UML中的结构建模 结构图显示建模系统的静态结构。关注系统的元件无须考虑时间,在系统内,静态结构通过显示类型和实例进行传播。除了显示系统类型和它们的实例,结构图至少也显示了这些元素间的一些关系,可能的话,甚至也显示内部结构。 贯穿整个软件生命周期,结构图对于各种团队成员都是有用的。一般而言,这些图支持设计验证和个体、团队间的设计交流。举例来说,业务分析师可以使用类或对象图来为当前的资产和资源建模,如分类账、产品或地理层次。架构设计师可以使用组件和部署图来测试和确认他们的设计是否充分。开发者可以使用类图来设计并为系统的代码(或即将成为代码的)类写文档。 UML中的结构建模包括类图、包图、对象图、构件图、组合结构图、部署图。 3.5.1类图 类图是UML中最基本也是最重要的一种视图,用来刻画软件中类等元素的静态结构和关系。在大多数UML模型中,这些类型包括类、接口、数据类型、组件。 1. 类 类(Class)是用来描述具有相同特征、约束、语义的一类对象,这些对象具有共同的属性和操作。类图中的一个类可以简单地只给出类名,也可以具体列出该类拥有的成员变量和方法,甚至更详细地描述可见性、方法参数、变量类型等信息。类的UML表示是一个矩形,垂直地分为三个区,顶部区域显示类的名字,中间区域列出类的属性,底部区域列出类的操作。 当在一个类图上画一个类元素时,必须要有顶端的区域,下面的两个区域是可选择的(当图描述仅仅用于显示分类器间关系的高层细节时,下面的两个区域是不必要的)。 图33Flight类的类图 图33中显示一个航线班机如何作为UML类建模。正如我们所能见到的名字是Flight,可以在中间区域看到Flight类的三个属性: flightNumber、departureTime和flightDuration。在底部区域中,可以看到Flight类有两个操作: delayFlight和getArrivalTime。 矩形代表一个类,该类图分为三层,第一层显示类的名称,如果是抽象类就要用斜体显示; 第二层是类的特性,通常就是字段和属性; 第三层是类的操作,通常是方法和行为。即从上到下三部分分别是类名、属性、操作。 类名是必须的,类如果有属性,则每一个属性都必须有一个名字,另外还可以有其他描述信息,如可见性、数据类型、默认值等。类如果有操作,则每一个操作也都有一个名字,其他可选信息包括可见性、参数的名字、参数类型、参数默认值和操作的返回值的类型等。 2. 抽象类 抽象类(Abstract class)是指一个类只提供操作名,而不对其进行实现。对这些操作的实现可以由其子类进行,并且不同的子类可以对同一操作具有不同的实现。抽象类和类的符号区别在于,抽象类的名称用斜体字符表示。图34给出了BankAccount类的类图。 3. 接口 如图35所示,“飞翔”矩形框表示一个接口图,与类图的区别主要是顶端有接口(Interface)的显示,第一行是接口名称,第二行是接口方法。接口还有另一种表示方法,俗称棒棒糖表示法,即唐老鸭类实现了“讲人话”的接口。 interface Ifly { void Fly(); } interface Ilanguage { void Speak(); } 图34BankAccount类的类图 图35接口的描述 图36关联关系的描述 4. 关联关系 关联关系(Association)描述了类的结构之间的关系,具有方向、名字、角色和多重性等信息。当关联是双向的,那么就可以用无向连线表示。一般的关联关系语义较弱,也有两种语义较强,分别是聚合与组合。图36给出了关联关系的描述。 5. 依赖关系 两个类之间存在依赖关系(Dependency),表明一个类使用或需要知道另一个类中包含的信息。它有多种表现形式,如绑定(bind)、友元(friend)等。模板类Stack定义了栈相关的操作; IntStack将参数T与实际类型int绑定,使得所有操作都针对int类型的数据。 如图37所示,依赖关系使用虚线箭头,表示“动物”“氧气”与“水”之间的依赖。动物有几大特征,例如有新陈代谢,能繁殖。而动物要有生命,就需要氧气、水及食物等。也就是说,动物依赖于氧气和水。 abstract class Animal { public bolism(Oxygen oxygen,Water water) { } } 图37依赖关系的描述 6. 聚合关系 聚合关系(Aggrengation)表明两个类的实例之间存在一种拥有或属于关系,可以看作是一种较弱的整体部分关系。在一个聚合关系中,子类实例可以比父类存在更长的时间。为了表现一个聚合关系,画一条从父类到部分类的实线,并在父类的关联末端画一个未填充菱形。 例如“大雁”和“雁群”这两个类,大雁是群居动物,每只大雁都属于一个雁群,一个雁群可以有多只大雁。所以,它们之间满足聚合关系。聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分。聚合关系用空心的菱形加上实线箭头表示。图38给出了聚合关系的描述。 class WideGooseAggregate { private WideGoose[] arrayWideGoose; //在雁群WideGooseAggregate类中,有大雁数组对象arrayWideGoose } 图38聚合关系的描述 7. 合成关系 合成(Composition)是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。合成关系用实心的菱形加上实线箭头来表示。另外,合成关系的连线两端还有一个数字“1”和数字“2”,这被称为基数,表明这一端的类可以有几个实例。例如“鸟”和“翅膀”这两个类中,鸟和翅膀类似整体和部分的关系,并且翅膀和鸟的生命周期是相同的,在这里,“鸟”类和其“翅膀”类就是合成关系。很显然,一只鸟应该有两只翅膀。如果一个类可能有无数个实例,则用“n”来表示。关联关系、聚合关系也可以有基数。图39给出了合成关系的描述。 class Bird { private Wing wing; public Bird() { wing=new Wing(); //在鸟Bird类中,初始化时,实例化翅膀Wing,它们之间同时生成 } } 聚合和合成的区别在于: 聚合关系是“hasa”关系,合成关系是“containsa”关系; 聚合关系表示整体与部分的关系比较弱,而合成关系比较强; 聚合关系中代表部分事物的对象与代表聚合事物的对象的生存期无关,一旦删除了聚合对象,不一定就删除了代表部分事物的对象; 合成关系中一旦删除了组合对象,同时也就删除了代表部分事物的对象。 8. 泛化关系 泛化关系(Generalization)在面向对象中一般称为继承关系,存在于父类与子类、父接口与子接口之间。图310给出了泛化关系的描述。 图39合成关系的描述 图310泛化关系的描述 3.5.2对象图 对象是类的实例,对象图可以看作是类图的实例,对象之间的连接(Link)是类之间关联关系的实例。对象图和类图的不同点在于,对象图显示类的多个对象实例,而不是实际的类。对象图显示某时刻对象和对象之间的关系,是类图的变化,一个对象图可看成一个类图的示例(example)。由于对象存在生命周期,因此对象图只能在系统某一时间段存在。 图311对象的表示 对象图中并无新的表示法(除了对象名下要加下画线以外),与类图中的表示法一样,可以认为,只有对象而无类的类图就是一个“对象图”。图311给出了对象的表示。 在对象图中,对象名可以有以下三种表示形式: (1) 对象名: 类名。 (2) : 类名。 (3) 对象名。 第一种即“对象名: 类名”格式,实质上,对象图几乎很少被用到,使用远没有类图广泛。 3.5.3构件图 构件图用于静态建模,是表示构件类型的组织以及各种构件之间依赖关系的图。构件图通过对构件间依赖关系的描述,来估计对系统构件的修改给系统可能带来的影响。由于基于构件的软件开发日益普及和应用,UML对构件图进行了较大的改进。构件的根本特征在于封装性、可复用性,内部结构被隐藏起来,只能通过接口向外部提供服务或请求外部的服务。 构件(Component)是系统中遵从一组接口且提供其实现的物理的、可替换的部分。构件能够完成独立功能,是软件系统的组成部分。在功能划分的软件系统中,软件被分成一个个的模块。随着面向对象技术的引用,软件系统被分成若干个子系统、构件。每个构件能够实现一定的功能,为其他构件提供使用接口,方便软件的复用。图312给出了一个绘制地铁售票信息系统的投币构件图的例子。 图312构件图 3.5.4部署图 部署图(Component Diagram)描述的是系统运行时的结构,展示了硬件的配置及其软件如何部署到网络结构中。一个系统模型只有一个部署图,部署图通常用来帮助理解分布式系统。部署图用于静态建模,是表示运行时过程节点结构、描述软件与硬件映射方法以及构件实例及其对象结构的图。图313给出了部署图的表示。 图313部署图的表示 3.6UML中的行为建模 行为建模被称为动态建模,主要用来刻画系统中的动态行为、过程、步骤。 UML行为建模中提供的视图可以从不同的侧面来描述软件系统的动态过程,例如,业务或算法过程与步骤、多个对象为完成一个场景而进行的交互、消息传递的过程、一个对象在生存周期中根据收到的不同事件进行响应的过程等。 3.6.1用例图 1. 用例图定义 用例图是指被称为参与者的外部用户所能观察到的系统功能的模型图。用例图列出系统中的用例和系统外的参与者,并显示哪个参与者参与了哪个用例的执行(或称为发起了哪个用例)。用例图多用于静态建模阶段(主要是业务建模和需求建模)。 2. 参与者 参与者(Actor)是指在系统外部与系统直接交互的人或事物(如另一个计算机系统或一些可运行的进程)。需要注意: (1) 参与者是角色(role)而不是具体的人,代表了参与者在与系统打交道的过程中所扮演的角色。所以在系统的实际运作中,一个实际用户可能对应系统的多个参与者。不同的用户也可以只对应于一个参与者,从而代表同一参与者的不同实例。 (2) 参与者作为外部用户(而不是内部)与系统发生交互作用,是它的主要特征。 3. 用例 用例(Use Case)是系统外部可见的一个系统功能单元。系统的功能由系统单元所提供,并通过一系列系统单元与一个或多个参与者之间交换的消息所表达。 1) 参与者与用例之间的关系 表示参与者与用例之间的交互、通信途径。有时候,关联也用带箭头的实线来表示,这样的表示能够显式地表明发起用例的是参与者。 2) 用例之间的关系 (1) 包含: 箭头指向的用例为被包含的用例,称为包含用例; 箭头出发的用例为基用例。包含用例是必选的,如果缺少包含用例,基用例就不完整; 包含用例必须被执行,不需要满足某种条件,其执行并不会改变基用例的行为。 (2) 扩展: 箭头指向的用例为被扩展的用例,称为扩展用例,箭头出发的用例为基用例。扩展用例是可选的,如果缺少扩展用例,不会影响到基用例的完整性,扩展用例在一定条件下才会执行,并且其执行会改变基用例的行为。 3) 参与者之间的关系 发出箭头的事物“is a”箭头指向的事物。泛化关系是一般和特殊关系,发出箭头的一方代表特殊的一方,箭头指向的一方代表一般的一方。特殊方继承了一般方的特性,并增加了新的特性。 4. 实例 1) 参与者之间的泛化关系 如图314所示,考虑以下用例图之间的关系。 图314用例图 在参与者之间,不存在泛化关系的情况下,各个参与者参与用例的情况如下: 经理参与用例管理人事和批准预算; 安全主管参与用例批准安全证书; 保安参与用例监视周边。由于安全主管与经理、安全主管与保安之间泛化关系的存在,意味着安全主管可以担任经理和保安的角色,即能够参与经理和保安参与的用例。这样,安全主管就可以参与全部四个用例。但经理或者保安却不能担任安全主管的角色,也就不能参与用例批准安全证书。 2) 用例之间扩展和包含关系 如图315所示,用例的上下文是短途旅行,但汽车的油不足以应付全部路程。那么,为汽车加油的动作在旅行的每个场景(事件流)中都会出现,不加油就不能完成旅行。吃饭则可以由驾驶人决定是否进行,不吃饭不会影响旅行的完成。 图315扩展功能的用例图 3.6.2顺序图 顺序图描述对象之间的动态交互关系,着重表现对象之间消息传递的时间顺序。顺序图有两个坐标轴,纵坐标轴表示时间,横坐标轴表示不同的对象。顺序图中的对象用一个矩形框表示,框内标有对象名(对象名的表示格式与对象图中相同)。从表示对象的矩形框向下的垂直虚线,是对象的“生命线”,用于表示在某段时间内该对象是存在的。 对象间的通信用对象生命线之间的水平消息线来表示,消息箭头的形状表明消息的类型(同步、异步或简单)。当收到消息时,接收对象立即开始执行活动,即对象被激活了。激活用对象生命线上的细长矩形框表示。消息通常用消息名和参数表来表示。消息还可以带有条件表达式,用以表示分支或决定是否发送消息。如果用条件表达式表示分支,则会有若干个互斥的箭头,即在某一时刻仅可发送分支中的一个消息。一个顺序图显示了一系列的对象和这些对象之间发送和接收的消息。 图316所示为图书管理系统中图书入库的顺序图。对于顺序图,往往在文字表述上会出现“当……时……”“首先”“然后”“接着”“……发出……消息”“……响应……消息”等词汇。例如,图316所示的顺序图可用文字表达为: 当管理人员把新书入库时,首先要求登录(输入用户名和口令),经系统的“注册表单”验证,若正确无误,则可继续下一步交互,否则拒绝该管理人员进入系统。若登录正确,管理人员可发出查询请求消息,系统的“图书入库表单”对象响应请求。若管理人员发出增加或删除库存图书请求,“库存图书”对象将响应该消息,找出数据库中的相关数据并执行相应的操作。此后,管理人员应按下提交键确认请求, 图316图书管理系统中图书入库的顺序图 “图书入库表单”接口对象应该响应该请求,并发出存储消息,再由“库存图书”对象响应存储消息,进行数据库存储操作。如果管理人员结束图书入库,发出退出系统的请求,则系统的“注册表单”接口对象响应请求,关闭系统。 3.6.3通信图 通信图是由协作图发展而来的。与顺序图不同,通信图主要关注参与交互的对象通过连接组成的结构。通信图的对象没有生命线,其消息及方向都附属于对象间的连接,并通过编号表示消息的顺序。Actor发送Print消息给Computer,Computer发送Print消息给PrintServer,如果打印机空闲,PrintServer发送Print消息给Printer。顺序图着重描述对象之间消息交换的时间顺序,通信图主要强调接收和发送消息的对象之间的结构组织。它们从不同的角度表达了系统中的交互之间是可以相互转换的。图317给出了通信图。 图317通信图 3.6.4交互概览图 交互概览图通过类似于活动图方式描述交互之间的流程,给出交互控制流的概览。在交互概览图中,节点不像活动图中那样是动作,而是一个交互图或是对交互图的引用。交互概览图,有以下两种形式。 (1) 一种是以活动图为主线,对活动图中某些重要活动节点进行细化,即用一些小的顺序图对重要活动节点进行细化,描述活动节点内的对象之间的交互。 (2) 另一种是以顺序图为主线,用活动图细化顺序图中某些重要对象,即用活动图描述重要对象的活动细节。 3.6.5时序图 时序图(Sequence Diagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。顺序图中显示的是参与交互的对象及其对象之间消息交互的顺序。时序图中包括的建模元素有对象(Actor)、生命线(Lifeline)、控制焦点(Focus of control)、消息(Message)等。时序图最常应用到实时或嵌入式系统的开发中,但并不局限于此。事实上,不管被建模的系统类型如何,对交互的准确时间进行建模是非常必要的。 时序图中,每个消息都有与其有关联的信息,准确描述了何时发送消息,消息的接收对象会花多长时间收到该消息,以及消息的接收对象需要多少时间处于某个特定状态。图318给出了时序图。 图318时序图 3.6.6状态图 状态图使用有穷状态变迁图的方式刻画系统或元素的离散行为,可以用来描述一个类的实例、子系统甚至整个系统在其生存周期内,所处状态如何随着外部激励而发生变化。图319给出了状态图。 图319状态图 状态图中包含以下四种状态: 初始状态、Available状态、Locked状态、Sold状态。状态之间的转移有以下几种。 (1) 初始状态转换到Available状态。 (2) 票被预订(lock): Available状态转换到Locked状态。 (3) 预定后付款(buy): Locked状态转换到Sold状态。 (4) 预定解除(unlock): Locked状态转换到Available状态。 (5) 预定过期(time out): Locked状态转换到Available状态。 (6) 直接购买(assigned to): Available状态转换到Sold状态。 (7) 换其他票(exchange),该票重有效: Sold状态转换到Available状态。 3.6.7活动图 活动图是UML用于对系统的动态行为建模的另一种常用工具,描述活动的顺序,展现从一个活动到另一个活动的控制流。本质上,活动图是一种流程图。活动图主要用于: 业务建模时详述业务用例,描述一项业务的执行过程,设计时描述操作的流程。UML的活动图中包含的图形元素有动作状态、活动状态、动作流、分支与合并、分叉与汇合、泳道和对象流等。图320给出了活动图。 图320活动图 活动图描述系统使用的活动、判定点和分支,看起来和流程图没什么两样,并且传统的流程图所能表示的内容,大多数情况下也可以用活动图表示,但是二者是有区别的,不能将两个概念混淆。 3.7UML工具 工具的重要性对于软件工程师来讲不言而喻,任何想法都需要借助工具来实现。最常用的UML工具包括: (1) Rational Rose。它是最有名的UML产品,大多数人将Rational Rose等同于UML工具,如同将可乐等同于可口可乐,如图321所示。就像一个戏剧导演设计一个剧本一样,一个软件设计师使用Rational Rose,以演员(数字)、使用拖放式符号的程序表中的有用案例元素(椭圆)、目标(矩形)、消息/关系(箭头)设计各种类,来创造(模型)一个应用的框架。当程序表被创建时,记录下这个程序表然后以设计师选择的C++、Visual Basic、Java、Oracle8、CORBA或者数据定义语言(Data Definition Language)来产生代码。需要指出的是,自从Rational被IBM收购之后,Rational Rose已经成为历史,作为UML1.4标准的产物,现在已经无法升级。其替代品是IBM的其他产品,如IBM Rational RSA(Rational Software Architect)、RSD(Rational Software Developer)等。 图321使用Rational Rose建模 (2) IBM Rational RSA(Rational Software Architect)。它是IBM的旗舰产品,通过和IBM其他产品的协调,支持软件开发的全生命周期开发,如图322所示。作为IBM软件开发平台的一部分,IBM RSA是IBM在2003年并购Rational以来首次发布的Rational产品,改进过的软件开发平台在集成、易用性上达到一个新的层次。RSA是一个基于Eclipse 的工具,支持开发者和架构师获得Eclipse平台可用性功能。同时,RSA超越了一个典型集成开发环境的功能,提供了丰富的建模、架构设计、挖掘能力。如果说有唯一的缺陷,可能是出奇的笨重和庞杂昂贵。 图322使用IBM Rational RSA建模 (3) Enterprise Architect(EA)。它是澳大利亚Sparx Systems公司的旗舰产品,覆盖了系统开发的整个周期,除了开发类模型之外,还包括事务进程分析、使用案例需求、动态模型、组件和布局、系统管理、非功能需求、用户界面设计、测试、维护。EA能完成从需求收集经步骤分析、模型设计,到测试和维护的整个软件开发过程,如图323所示。EA功能完善,是低价产品。 图323使用Enterprise Architect建模 (4) StarUML(SU)。它是一款开放源码、完全免费的UML开发工具,是由韩国公司主导开发,用Delphi写的产品,可以直接到StarUML网站下载,如图324所示。StarUML可绘制UML中的用例图、类图、序列图、状态图、活动图、通信图、构件图、部署图、复合结构图,可导出jpg、jpeg、bmp、emf、wmf等格式影像文件。同时,还支持23种GoF设计模式,以及三种常用的EJB模式EntityEJB、MessageDrivenEJB、SessionEJB。StarUML结合了模式和自动生成代码的功能,方便落实设计。 图324使用StarUML建模 (5) Trufun Kant Studio。它是基于TrufunPlato UML 2建模工具、Trufun Kant MDA开发平台、Trufun Cicero数据库建模工具所集成为一体的面向对象开发全过程的应用开发平台。作为一个“一站式”的企业级建模及设计开发平台,Trufun Kant Studio能帮助企业快速、高效地进行企业应用系统构建及再工程,企业可以有效开发、管理各种解决方案,从定义业务需求到分析、设计,以至集成所有现代RDBMS和Java、VS.NET、C++的开发。作为国产的UML工具,目前在国内大多数高校得到了广泛的应用。对企业和高校而言,其最独特的杀伤性武器是支持广泛、实用性强,而且能够帮助企业引入实训和咨询,一步到位解决产品和具体应用问题。 还有其他一些不太常用的UML工具,如ArgoUML、PowerDesigner、Visual UML等。 3.8小结 本章详细介绍了软件体系结构和统一建模语言UML及其开发过程。UML是一种通用语言,被软件架构师和开发人员用来描述、指定、设计、记录软件系统的现有或新业务流程、结构和行为。UML可以应用于不同的领域,如银行、金融、互联网、航空航天、医疗保健等,可以用于所有主要的对象和组件软件开发方法以及各种面向对象的开发语言。软件体系结构模型对系统的用例、类、对象、接口、构件以及相互间的协作进行描述。面向对象系统的开发过程以体系结构为中心,以用例为驱动,是一个反复、渐增的过程。UML作为一种快速进行系统分析和设计的技术支持,适用于以面向对象技术来描述的系统及其整个系统开发的不同阶段。本章对UML主要从结构建模和行为建模两方面进行了阐述,介绍了类图、对象图、构件图、部署图、用例图、顺序图、通信图、时序图、交互概览图、状态图及活动图。 3.9思考题 (1) 在整个开发过程中,UML主要起到什么作用? (2) 如何利用模式解决面向对象系统分析与设计中遇到的问题? (3) UML中都包含哪些图?简述这些图的作用。 (4) 简述用例之间的关系。 (5) 神舟六号是神舟系列飞船的一种,它由轨道舱、返回舱、推进舱和逃逸救生塔组合。航天员使用返回舱来驾驭飞船。轨道舱是航天员工作和休息的场所。在紧急情况下,航天员使用逃逸救生塔逃离。飞船的两侧有多个太阳能电池翼,它为飞船提供电能。根据以上描述画出能正确表示它们之间关系的UML图。 (6) 某个网上银行的用户登录过程如下: 用户先填写用户名和口令,要求登录。如果用户名和密码正确,则要求输入一个验证码。此时该用户的手机上将接受一个短信,包含一个验证码,用户再将此码填入下一个页面,再提交服务器。如果验证码正确,则能正常登录。验证码一次有效。用一个时序图描述这个过程。