面向对象方法是一种非常实用的软件开发方法,它一出现就受到软件技术人员的青睐,现已成为计算机科学研究的一个重要领域,并逐渐成为软件开发的一种主要方法。面向对象方法以客观世界中的对象为中心,其分析和设计思想符合人们的思维方式,分析和设计的结构与客观世界的实际比较接近,容易被人们接受。在面向对象方法中,分析和设计的界面并不明显,它们采用相同的符号表示,能够方便地从分析阶段平滑地过渡到设计阶段。此外,在现实生活中,用户的需求经常会发生变化,但客观世界的对象及对象间的关系比较稳定,因此用面向对象方法分析和设计的结构也相对比较稳定。
1 面向对象的基本概念
1.1 对象和类
对象是系统中用来描述客观事物的一个实体,它由对象标识(名称) 、 属性(状态 、 数据 、 成员变量)和服务(操作 、 行为 、 方法)三个要素组成,它们被封装为一个整体,以接口的形式对外提供服务。
在现实世界中,每个实体都是对象,如学生 、 书籍 、 收音机等;每个对象都有它的操作,例如书籍的页数,收音机的频道 、 按钮等属性,以及收音机的切换频道等操作。而类则是对具有相同属性和服务的一个或一组对象的抽象。
类与对象是抽象描述和具体实例的关系,一个具体的对象被称为类的一个实例。
在系统设计过程中,类可以分为三种类型,分别是实体类 、 边界类和控制类。
(1)实体类
实体类映射需求中的每个实体,实体类保存需要存储在永久存储体中的信息,例如,在线教育平台系统可以提取出学员类和课程类,它们都属于实体类。实体类通常都是永久性的,它们所具有的属性和关系是长期需要的,有时甚至在系统的整个生存期都需要。
实体类是对用户来说最有意义的类,通常采用业务领域术语命名,一般来说是一个名词,在用例模型向领域模型的转化中,一个参与者一般对应于实体类。
通常可以从数据库表(需要持久存储)对应的名词着手来找寻实体类。通常情况下,实体类一定有属性,但不一定有操作。
(2)控制类
控制类是用于控制用例工作的类,一般是由动宾结构的短语( “ 动词 + 名词 ” 或 “ 名词 + 动词 ” )转化来的名词,例如,用例 “ 身份验证 ” 可以对应于一个控制类 “ 身份验证器 ” ,它提供了与身份验证相关的所有操作。
控制类用于对一个或几个用例所特有的控制行为进行建模,控制对象(控制类的实例)通常控制其他对象,因此,它们的行为具有协调性。
控制类将用例的特有行为进行封装,控制对象的行为与特定用例的实现密切相关,当系统执行用例的时候,就产生了一个控制对象,控制对象经常在其对应的用例执行完毕后消亡。通常情况下,控制类没有属性,但一定有方法。
(3)边界类
边界类用于封装在用例内 、 外流动的信息或数据流。边界类位于系统与外界的交接处,包括所有窗体 、 报表 、 打印机和扫描仪等硬件的接口,以及与其他系统的接口。要寻找和定义边界类,可以检查用例模型,每个参与者和用例交互至少要有一个边界类,边界类使参与者能与系统交互。
边界类是一种用于对系统外部环境与其内部运作之间的交互进行建模的类。常见的边界类有窗口 、 通信协议 、 打印机接口 、 传感器和终端等。实际上,在系统设计时,产生的报表都可以作为边界类来处理。边界类用于系统接口与系统外部进行交互,边界对象将系统与其外部环境的变更(例如,与其他系统的接口的变更 、 用户需求的变更等)分隔开,使这些变更不会对系统的其他部分造成影响。通常情况下,边界类可以既有属性也有方法。
1.2 继承与泛化
继承是面向对象方法中重要的概念,用来说明特殊类(子类)与一般类(父类)的关系,而通常用泛化来说明一般类与特殊类的关系,也就是说它们是一对多关系。如图 1 所示, “ 交通工具 ” 是 “ 自行车 ” 和 “ 小轿车 ” 的泛化; “ 自行车 ” 和 “ 小轿车 ” 从 “ 交通工具 ” 中继承。
1.3 多态与重载
多态(即多种形式)性是指一般类中定义的属性或服务被特殊类继承后,可以具有不同的数据类型或表现出不同的行为,通常是使用重载和改写两项技术来实现的。一般有4种不同形式的多态,如下表所示。
(1)重载(专用多态)
重载也称为过载、重置。指的是同一个函数名称有多种实现方式,一般在编译时基于类型签名来区分。
class xxx{
public void a(int x);
public void a(int x,int y);
}
(2)改写(包含多态)
改写是重载的特殊情况,发生在父子类关系中。方法签名相同,但子类改写了父类的方法实现。
class Parent{
public void a(int x){
System.out.print("parent implement.")
}
}
class Child extends Parent{
public void a(int x){
System.out.print("child implement.")
}
}
(3)多态变量(强制多态)
也叫赋值多态或者强制多态。
声明是一种类型,但变量实际上却包含另一种类型。
Parent p = new Child();
(4)泛型(参数多态)
也叫模板或者参数多态。这是一种可用于创建通用工具的方法,可在特定场合将其特化。
public class XXX {
private T val;
}
参数多态和包含多态称为通用多态,重载多态和强制多态称为特定多态。
虽然重载和改写都是在多种潜在的函数体中,选择和调用某一个函数或方法并对其进行执行,但它们的本质区别在于:重载是编译时执行的(静态绑定),而改写则是运行时选择的(动态绑定)。
1.4 模板类
也称为类属类,它用来实现参数多态机制。一个类属类是关于一组类的一个特性抽象,它强调的是这些类的成员特征中与具体类型无关的那些部分,而用变元来表示与具体类型有关的那些部分。
1.5 消息和消息通信
消息就是向对象发出的服务请求,它通常包括提供服务的对象标识 、 消息名 、 输入信息和回答信息。消息通信则是面向对象方法学中的一个重要原则,它与对象的封装原则密不可分,为对象间提供了唯一合法的动态联系的途径。
2 面向对象分析
面向对象分析的目标是开发一系列模型,这些模型描述计算机软件,当它工作时以满足一组客户定义的需求。对象技术的流行,演化出了数十种不同的 OOA 方法,每个方法都引入了一个产品或系统分析的过程 、 一组过程演化的模型及使软件工程师能够以一致的方式创建每个模型的符号体系。其中比较流行的方法包括OMT 、 OOA 、 OOSE 、 Booch方法等,而OMT 、 OOSE 、 Booch最后则统一成为 UML。
2.1 OOA/OOD 方法
这是由 Peter Coad 和 Edward Yourdon 提出的, OOA 模型中包括主题 、 对象类 、 结构 、 属性和服务5个层次,需经过标识对象类 、 标识结构与关联(包括继承 、 聚合 、 组合 、 实例化等) 、 划分主题 、 定义属性 、 定义服务5个步骤来完成整个分析工作 。OOD 中将继续贯穿 OOA 中的5个层次和5个活动,它由人机交互部件 、 问题域部件 、 任务管理部件 、 数据管理部件4个部分组成,其主要的活动就是这4个部件的设计工作。
- 设计问题域部分: OOA 的结果恰好是 OOD 的问题域部件,分析的结果在 OOD 中可以被改动或增补,但基于问题域的总体组织框架是长时间稳定的;
- 设计人机交互部件:人机交互部件在上述结果中加入人机交互的设计和交互的细节,包括窗口和输出报告的设计。可以用原型来帮助实际交互机制进行开发和选择;
- 设计任务管理部分:这部分主要是识别事件驱动任务,识别时钟驱动任务,识别优先任务和关键任务,识别协调者,审查每个任务并定义每个任务。
- 设计数据管理部分:数据管理部分提供了在数据管理系统中存储和检索对象的基本结构,其目的是隔离数据管理方法对其他部分的影响。
2.2 Booch 方法
Booch 认为软件开发是一个螺旋上升的过程,每个周期中包括标识类和对象 、 确定类和对象的含义 、 标识关系 、 说明每个类的接口和实现4个步骤。它的模型中主要包括如下表所示的几种图形。
静态模型 | 动态模型 | |
---|---|---|
逻辑模型 | 类图、对象图 | 状态转换图、时序图 |
物理模型 | 模块图、进程图 |
Booch 方法的开发过程是一个迭代的 、 渐进式的系统开发过程,它可以分为宏过程和微过程两类。
宏过程用于控制微过程,是复盖几个月或几周所进行的活动,它包括负责建立核心需求的概念化,为所期望的行为建立模型的分析,建立架构的设计,形成实现的进化,以及管理软件交付使用的维护等5个主要活动。
而微过程则基本上代表了开发人员的日常活动,它由4个重要 、 没有顺序关系的步骤组成:在给定的抽象层次上识别出类和对象,识别出这些类和对象的语义,识别出类间和对象间的关系,实现类和对象。
2.3 OMT 方法
OMT 是对象建模技术的缩写,它是由 Jam Rambaugh 及其同事合作开发的,它主要用于分析 、 系统设计和对象设计。包括对象模型(静态的 、 结构化的系统的 “ 数据 ” 性质,通常采用类图) 、 动态模型(瞬时的 、 行为化的系统 “ 控制 ” 性质,通常使用状态图)和功能模型(表示变化的系统的 “ 功能 ” 性质,通常使用数据流图)。 OMT 方法的三大模型如下表所示。
模型 | 说明 | 特征 | 图形 |
---|---|---|---|
对象模型 | 描述系统中对象的静态结构、对象间的关系、属性及操作。 | 静态、结构式、系统“”数据“” | 对象图 |
动态模型 | 描述系统中与时间和操作顺序有关的系统特征。比如触发事件、事件序列以及确定事件先后关系的状态等等。 | 瞬时、行为式、系统“”控制“ | 状态图 |
功能模型 | 描述与值变换有关的系统特征。 | 功能、映射、约束以及函数依赖 | 数据流图 |
2.4 OOSE 方法
OOSE (Object-oriented software engineering)是面向对象软件工程的缩写,它是由 Iva rJacobson 提出的。它在 OMT 的基础上,对功能模型进行了补充,提出了 “ 用例 ” 的概念,最终取代数据流图进行需求分析和建立功能模型。
3 统一建模语言
统一建模语言( Unified Modeling Language , UML )是用于系统的可视化建模语言,它将 OMT 、 OOSE 和 Booch 方法中的建模语言和方法有机地融合在一起,是国际统一的软件建模标准。虽然它源于 OO 软件系统建模领域,但由于其内建了大量扩展机制,也可以应用于更多的领域中,例如工作流程 、 业务领域等。
3.1 UML 是什么
- UML 是一种语言: UML 在软件领域中的地位与价值就像 “1 、 2 、 3 ” 等符号在数学领域中的地位一样。它为软件开发人员之间提供了一种用于交流的词汇表和一种用于软件蓝图的标准语言。
- UML 是一种可视化语言: UML 只是一组图形符号,它的每个符号都有明确语义,是一种直观 、 可视化的语言。
- UML 是一种可用于详细描述的语言: UML 所建的模型是精确的 、 无歧义和完整的,因此适合于对所有重要的分析 、 设计和实现决策进行详细描述。
- UML 是一种构造语言: UML 虽然不是一种可视化的编程语言,但其与各种编程语言直接相连,而且有较好的映射关系,这种映射允许进行正向工程 、 逆向工程。
- UML 是一种文档化语言:它适合于建立系统架构及其所有的细节文档。
3.2 UML 的结构
UML 由构造块、公共机制和架构三个部分组成。
(1)构造块
构造块也就是基本的 UML 建模元素(事物)、关系和图。
- 建模元素:包括结构事物(类 、 接口 、 协作 、 用例 、 活动类 、 组件 、 节点等) 、 行为事物(交互 、 状态机) 、 分组事物(包) 、 注释事物。
- 关系:包括关联关系 、 依赖关系 、 泛化关系 、 实现关系。
- 图: UML2.0 包括 14 种不同的图,分为表示系统静态结构的静态模型(包括类图 、 对象图 、 包图 、 构件图 、 部署图 、 制品图),以及表示系统动态结构的动态模型(包括对象图 、 用例图 、 顺序图 、 通信图 、 定时图 、 状态图 、 活动图 、 交互概览图)。
(2)公共机制
公共机制是指达到特定目标的公共 UML 方法,主要包括规格说明 、 修饰 、 公共分类和扩展机制4种。
- 规格说明:规格说明是元素语义的文本描述,它是模型的重要组成部分。修饰: UML 为每一个模型元素设置了一个简单的记号,还可以通过修饰来表达更多的信息。
- 公共分类:包括类元与实体(类元表示概念,而实体表示具体的实体) 、 接口和实现(接口用来定义契约,而实现就是具体的内容)两组公共分类。
- 扩展机制:包括约束(添加新规则来扩展元素的语义) 、 构造型(用于定义新的 UML 建模元素) 、 标记值(添加新的特殊信息来扩展模型元素的规格说明)。
(3)架构
UML 对系统架构的定义是:系统的组织结构,包括系统分解的组成部分 、 它们的关联性 、 交互 、 机制和指导原则,这些提供系统设计的信息。而具体来说,就是指5个系统视图。
- 逻辑视图:以问题域的语汇组成的类和对象集合。
- 进程视图:可执行线程和进程作为活动类的建模,它是逻辑视图的一次执行实例。
- 实现视图:对组成基于系统的物理代码的文件和组件进行建模。
- 部署视图:把组件物理地部署到一组物理的、可计算的节点上。
- 用例视图:最基本的需求分析模型。
3.3 用例图基础
用例是什么呢? Ivar Jacobson 是这样描述的: “ 用例实例是在系统中执行的一系列动作,这些动作将生成特定参与者可见的价值结果。一个用例定义一组用例实例 。 ”
首先,从定义中得知用例是由一组用例实例组成的,用例实例也就是常说的 “ 使用场景 ” ,就是用户使用系统的一个实际的 、 特定的场景。其次,可以知道,用例应该给参与者带来可见的价值,这点很关键。最后,用例是在系统中的。而用例模型描述的是外部参与者所理解的系统功能。用例模型用于需求分析阶段,它的建立是系统开发者和用户反复讨论的结果,表明了开发者和用户对需求规格达成的共识。图 2 就是一个用例图的例子。
extend 拓展关系:用于拓展用例对基础用例的增强。拓展用例是在特定条件出现时,才会被执行的用例。使用带有“拓展”的箭头来表示拓展用例,方向由拓展用例指向基础用例。
(1)参与者
参与者代表与系统接口的任何事物或人,它是指代表某一种特定功能的角色,因此,参与者都是虚拟的概念。在 UML 中,用一个小人表示参与者。
图 2 中的 “ 图书管理员 ” 就是参与者。对于该系统来说,可能可以充当图书管理员角色的有多个人,由于他们对系统均起着相同的作用,扮演相同的角色,因此只用一个参与者来表示。切忌不要为每一个可能与系统交互的真人画出一个参与者。
(2)用例
用例是对系统行为的动态描述,它可以促进设计人员 、 开发人员与用户的沟通,理解正确的需求,还可以划分系统与外部实体的界限,是系统设计的起点。在识别出参与者之后,可以使用下列问题帮助识别用例。
- 每个参与者的任务是什么?
- 有参与者将要创建、存储、修改、删除或读取系统中的信息吗?
- 什么用例会创建、存储、修改、删除或读取这个信息?
- 参与者需要通知系统外部的突然变化吗?
- 需要通知参与者系统中正在发生的事情吗?
- 什么用例将支持和维护系统?
- 所有的功能需求都对应到用例中了吗?
- 系统需要何种输入/输出?输入从何处来?输出到何处?
- 当前运行系统的主要问题是什么?
(3)包含和扩展
两个用例之间的关系可以主要概括为两种情况。一种是用于重用的包含关系,用构造型 <
包含关系:当可以从两个或两个以上的原始用例中提取公共行为,或者发现能够使用一个组件来实现某一个用例的部分功能是很重要的事时,应该使用包含关系来表示。所提取出来的公共行为称为抽象用例。包含关系的例子如图 3 所示。
扩展关系:如果一个用例明显地混合了两种或两种以上的不同场景,即根据情况可能发生多种事情。可以将这个用例分为一个主用例和一个或多个辅用例,描述可能更加清晰。扩展关系的例子如图 4 所示。
此处介绍的包含和扩展关系属于用例之间所特有的关系,因为用例也是 UML 的一种结构事物,因此,事物之间的 4 种基本关系对用例也是适用的。
3.4 类图和对象图基础
在面向对象建模技术中,将客观世界的实体映射为对象,并归纳成一个个类。类 、 对象和它们之间的关联是面向对象技术中最基本的元素。对于一个想要描述的系统,其类模型和对象模型揭示了系统的结构。在 UML 中,类和对象模型分别由类图和对象图表示。类图技术是 OO 方法的核心。图 5 是一个类图的示例。
图中的空心菱形箭头表示聚合关系(aggregation),聚合关系是关联关系的一种,是强的关联关系。聚合关系是整体和个体的关系。关联关系的两个类处于同一层次上,而聚合关系两个类处于不同的层次,一个是整体,一个是部分。箭头指向整体。
图中的空心三角箭头表示为类与类之间的继承关系,接口与接口之间的继承,类对接口的实现关系,也叫泛化关系。箭头指向父类。如果父类是类,用实线;如果父类是接口,用虚线。
3.3.1 类和对象
对象与人们对客观世界的理解相关。人们通常用对象描述客观世界中某个具体的实体。所谓类是对一类具有相同特征的对象的描述。而对象是类的实例。在 UML 中,类的可视化表示为一个划分成三个格子的长方形(下面两个格子可省略)。图 5 中, “ 书籍 ” 、 “ 借阅记录 ” 等都是一个类。
类的获取和命名:最顶部的格子包含类的名字。类的命名应尽量用应用领域中的术语,应明确 、 无歧义,以利于开发人员与用户之间的相互理解和交流。
类的属性:中间的格子包含类的属性,用以描述该类对象的共同特点。该项可省略。图 5 中 “ 书籍 ” 类有 “ 书名 ” 、 “ 书号 ” 等属性。 UML 规定类的属性的语法为: “ 可见性属性名:类型 = 默认值 { 约束特性} ” 。可见性包括Public 、 Private和 Protected ,分别用+ 、 - 、 #号表示。
类型表示该属性的种类:它可以是基本数据类型,例如整数 、 实数 、 布尔型等,也可以是用户自定义的类型。一般它由所涉及的程序设计语言确定。
约束特性则是用户对该属性性质的一个约束说明。例如 “ { 只读 }” 说明该属性是只读属性。
类的操作( Operation ):该项可省略。操作用于修改 、 检索类的属性或执行某些动作。操作通常也被称为功能,但是它们被约束在类的内部,只能作用到该类的对象上。操作名 、 返回类型和参数表组成操作界面 。
UML 规定操作的语法为: “ 可见性:操作名(参数表):返回类型 { 约束特性 }” 。类图描述了类和类之间的静态关系。定义了类之后,就可以定义类之间的各种关系了。
3.3.2 类之间的关系
在建立抽象模型时,会发现很少有类会单独存在,大多数都将会以某种方式互相协作,因此还需要描述这些类之间的关系。关系是事物间的连接,在面向对象建模中,有4个很重要的关系。
(1)依赖关系(Dependency)
有两个元素 X 、 Y ,如果修改元素 X 的定义可能会引起对另一个元素 Y 的定义的修改,则称元素 Y 依赖于元素 X 。在 UML 中,使用带箭头的虚线表示依赖关系。带箭头的虚线,指向依赖者。
在类中,依赖由多种原因引起,如:一个类向另一个类发消息;一个类是另一个类的数据成员;一个类是另一个类的某个操作参数。如果一个类的界面改变,它发出的任何消息可能不再合法。
(2)泛化关系
泛化关系描述了一般事物与该事物中的特殊种类之间的关系,也就是父类与子类之间的关系。继承关系是泛化关系的反关系,也就是说子类是从父类中继承的,而父类则是子类的泛化。在 UML 中,使用带空心箭头的实线表示,箭头指向父类。
在 UML 中,对泛化关系有 3 个要求:
- 子类应与父类完全一致,父类所具有的关联、属性和操作,子类都应具有。
- 子类中除了与父类一致的信息外,还包括额外的信息。
- 可以使用父类实例的地方,也可以使用子类实例。
(3)关联关系
关联表示两个类之间存在某种语义上的联系。例如,一个人为一家公司工作,一家公司有许多办公室。就认为人和公司 、 公司和办公室之间存在某种语义上的联系。
关联关系提供了通信的路径,它是所有关系中最通用的 、 语义最弱的。在 UML 中,用一条实线来表示关联关系。
(4)聚合关系
聚合是一种特殊形式的关联。聚合表示类之间的关系是整体与部分的关系。例如一辆轿车包含四个车轮 、 一个方向盘 、 一个发动机和一个底盘,就是聚合的一个例子。
在 UML 中,用一个带空心菱形的实线表示,空心菱形指向的是代表 “ 整体 ” 的类。
如果聚合关系中的表示“部分”的类的存在,与表示“整体”的类有着紧密的关系,例如“公司”与“部门”之间的关系,那么就应该使用“组合”关系来表示。
在 UML 中, 用带有实心菱形的实线表示,菱形指向的是代表“整体”的类。
聚合关系是指部分与整体的生命周期可以不相同,而组合关系则是 指部分与整体的生命周期是相同的。
(5)实现关系
实现关系是用来规定接口和实现接口的类或组件之间的关系的。接口是操作的集合,这些操作用于规定类或组件的服务。
在 UML 中,用一个带空心箭头的虚线表示。
3.3.3 多重性问题
重复度又称多重性,多重性表示为一个整数范围 n … m ,整数 n 定义所连接的最少对象的数目,而 m 则为最多对象数(当不知道确切的最大数时,最大数用 * 号表示)。最常见的多重性有: 0…1 ;0… * ; 1…1 ;1… * ; * 。
多重性是用来说明关联的两个类之间的数量关系的,例如:
- 书与借书记录之间的关系,就应该是1对 0 …1 的关系,也就是一本书可以有 0 个或1个借书记录。
- 经理与员工之间的关系,则应为1对 0 …*的关系,也就是一个经理可以领导 0 个或多个员工。
- 学生与选修课程之间的关系,就可以表示为 0 …对 1… 的关系,也就是一个学生可以选择1门或多门课程,而一门课程可以有 0 个或多个学生选修。
3.3.4 类图
类图描述类和类之间的静态关系。与数据模型不同,它不仅显示了信息的结构,同时还描述了系统的行为。类图是定义其他图的基础。
3.3.5 对象图
UML 中对象图与类图具有相同的表示形式。对象图可以看作是类图的一个实例。对象是类的实例;对象之间的链( Link )是类之间的关联的实例。对象与类的图形表示相似,均为划分成两个格子的长方形(下面的格子可省略)。上面的格子是对象名,对象名下有下画线;下面的格子记录属性值。链的图形表示与关联相似。对象图常用于表示复杂类图的一个实例。
3.5 交互图基础
交互图是表示各组对象如何依某种行为进行协作的模型。通常可以使用一个交互图来表示和说明一个用例的行为。在 UML 中,包括3种不同形式的交互图,强调对象交互行为顺序的顺序图,强调对象协作的通信图( UML 1.X版本中称为 “ 协作图 ” ),强调消息的具体时间的定时图,它们之间没有什么本质不同,只是排版不尽相同而已。
3.5.1 顺序图
顺序图用来描述对象之间动态的交互关系,着重体现对象间消息传递的时间顺序。顺序图允许直观地表示出对象的生存期,在生存期内,对象可以对输入消息做出响应,并且可以发送信息。
图 10 是一个顺序图的示例。如图 10 所示,顺序图存在两个轴,水平轴表示不同的对象,即图中的 Client 、 Factory 、 Product 等;而垂直轴表示时间,表示对象及类的生命周期。对象间的通信通过在对象的生命线间画消息来表示。消息的箭头指明消息的类型。
顺序图中的消息可以是信号 、 操作调用或类似于 C++ 中的 RPC ( Remote Procedure Calls )和 Java 中的 RMI ( Remote Method Invocation )。当收到消息时,接收对象立即开始执行活动,即对象被激活了。通过在对象生命线上显示一个细长矩形框来表示激活。
消息可以用消息名及参数来标识,消息也可带有顺序号。消息还可带有条件表达式,表示分支或决定是否发送消息。如果用于表示分支,则每个分支是相互排斥的,即在某一时刻仅可发送分支中的一个消息。
3.5.2 通信图
通信图用于描述相互合作的对象间的交互关系和链接关系。虽然顺序图和通信图都用来描述对象间的交互关系,但侧重点不一样。顺序图着重体现交互的时间顺序,通信图则着重体现交互对象间的静态链接关系。
图 11 就是与图 10 相对应的通信图,可以从图 11 中很明显地发现它与顺序图之间的异同点。
3.5.3 定时图
如果要表示的交互具有很强的时间特性(例如,现实生活中的电子工程 、 实时控制等系统中),在 UML 1.X 中是无法有效地表示出来的。而在 UML 2.0 中引入了一种新的交互图来解决这类问题,这就是着重表示定时约束的定时图。
根据 UML 的定义,定时图实际上是一种特殊形式的顺序图(即一种变化),它与顺序图的区别主要体现在以下几个方面。
- 坐标轴交换了位置,改为从左到右来表示时间的推移。
- 用生命线的“凹下凸起”来表示状态的变化,每个水平位置代表一种不同的状态,状态的顺序可以有意义、也可以没有意义。
- 生命线可以跟在一根线后面,在这根线上显示一些不同的状态值。
- 可以显示一个度量时间值的标尺,用刻度来表示时间间隔。
图 12 是一个定时图的实际例子,其中小黑点加曲线表示的是注释。它用来表示一个电子门禁系统的控制逻辑,该门禁系统包括门(物理的门) 、 智能读卡器(读取用户的智能卡信息) 、 处理器(用来处理是否开门的判断)。
在这个例子中,它所表示的意思是一开始读卡器是启用的(等用户来刷卡) 、 处理器是空闲的(没有验证的请求) 、 门是关的;接着,当用户刷卡时,读卡器就进入了 “ 等待校验 ” 的状态,并发一个消息给处理器,处理器就进入了校验状态。如果校验通过,就发送一个 “ 禁用 ” 消息给读卡器(因为门开的时候,读卡器就可以不工作),使读卡器进入禁用状态;并且自己转入启用状态,这时门的状态变成了 “ 开 ” 。而门 “ 开 ” 了 40 秒钟(根据时间刻度得知)之后,处理器将会把它再次 “ 关 ” 上,并且发送一个 “ 启用 ” 消息给读卡器(门关了,读卡器开始重新工作),这时读卡器再次进入启用状态,而处理器已经又回到了空闲状态。
从上面的例子中,不难看出定时图所包含的图元并不多,主要包括生命线 、 状态 、 状态变迁 、 消息 、 时间刻度,可以根据自身的需要来使用它。
3.6 状态图基础状态图
用来描述一个特定对象的所有可能状态及其引起状态转移的事件。大多数面向对象技术都用状态图表示单个对象在其生命周期中的行为。一个状态图包括一系列的状态及状态之间的转移。图 13 是一个数码冲印店的订单状态图实例。
状态图包括以下部分。
- 状态:又称为中间状态,用圆角矩形框表示;
- 初始状态:又称为初态,用一个黑色的实心圆圈表示,在一张状态图中只能够有一个初始状态;
- 结束状态:又称为终态,在黑色的实心圆圈外面套上一个空心圆,在一张状态图中可能有多 个结束状态;
- 状态转移:用箭头说明状态的转移情况,并用文字说明引发这个状态变化的相应事件是什么。
一个状态也可能被细分为多个子状态,那么如果将这些子状态都描绘出来的话,那这个状态就是复合状态。状态图适合用于表述在不同用例之间的对象行为,但并不适合用于表述包括若干用例协作的对象行为。
通常不会需要对系统中的每一个类绘制相应的状态图,而通常会在业务流程 、 控制对象 、 用户界面的设计方面使用状态图。
3.7 活动图基础
活动图的应用非常广泛,它既可用来描述操作(类的方法)的行为,也可以描述用例和对象内部的工作过程。活动图是由状态图变化而来的,它们各自用于不同的目的。活动图依据对象状态的变化来捕获动作(将要执行的工作或活动)与动作的结果。活动图中一个活动结束后将立即进入下一个活动(在状态图中状态的变迁可能需要事件的触发)。
(1)基本活动图
如图 14 所示,活动图与状态图类似,包括了初始状态 、 终止状态,以及中间的活动状态,每个活动之间,也就是一种状态的变迁。在活动图中,还引入了以下几个概念。
- 判定:说明基于某些表达式的选择性路径,在 UML 中使用菱形表示。
- 分支与组合:由于活动图建模时,经常会遇到并发流,因此在 UML 中引入了如上图 14 所示的粗线来表示分支和组合。
(2)带泳道的活动图
在前面说明的基本活动图中,虽然能够描述系统发生了什么,但没有说明该项活动由谁来完成。而针对 OOP 而言,这就意味着活动图没有描述出各个活动由哪个类来完成。要想解决这个问题,可以通过泳道来解决这一问题。它将活动图的逻辑描述与顺序图 、 协作图的责任描述结合起来。图 15 是一个使用了泳道的例子。
(3)对象流
在活动图中可以出现对象。对象可以作为活动的输入或输出,对象与活动间的输入 / 输出关系由虚线箭头来表示。如果仅表示对象受到某一活动的影响,则可用不带箭头的虚线来连接对象与活动。
(4)信号
在活动图中可以表示信号的发送与接收,分别用发送和接收标识来表示。发送和接收标识也可与对象相连,用于表示消息的发送者和接收者。
3.8 构件图基础
构件图是面向对象系统的物理方面进行建模要用的两种图之一。它可以有效地显示一组构件,以及它们之间的关系。构件图中通常包括构件 、 接口及各种关系。图 16 就是一个构件图的例子。
通常构件指的是源代码文件 、 二进制代码文件和可执行文件等。而构件图就是用来显示编译 、 链接或执行时构件之间的依赖关系的。例如,在图 16 中,就是说明 QueryClient.exe 将通过调用 QueryServer.exe 来完成相应的功能,而 QueryServer.exe 则需要 Find.exe 的支持, Find.exe 在实现时调用了 Query.dll。
通常来说,可以使用构件图完成以下工作。
- 对源代码进行建模:这样可以清晰地表示出各个不同源程序文件之间的关系。
- 对可执行体的发布建模:如图 16 所示,将清晰地表示出各个可执行文件、DLL 文件之间的关系。
- 对物理数据库建模:用来表示各种类型的数据库、表之间的关系。
- 对可调整的系统建模:例如对应用了负载均衡、故障恢复等系统的建模。
在绘制构件图时,应该注意侧重于描述系统的静态实现视图的一个方面,图形不要过于简化,应该为构件图取一个直观的名称,在绘制时避免产生线的交叉。
3.9 部署图基础
部署图,也称为实施图,它和构件图一样,是面向对象系统的物理方面建模的两种图之一。构件图是说明构件之间的逻辑关系,而部署图则是在此基础上更进一步地描述系统硬件的物理拓扑结构及在此结构上执行的软件。部署图可以显示计算结点的拓扑结构和通信路径 、 结点上运行的软件构件,常用于帮助理解分布式系统。
图 17 就是与图 16 对应的部署图,这样的图示可以使系统的安装 、 部署更为简单。
在部署图中,通常包括以下一些关键的组成部分。
(1)节点和连接。节点代表一个物理设备及其上运行的软件系统,如一台 UNIX 主机 、 一个 PC 终端 、 一台打印机 、 一个传感器等。
如图 17 所示, “ 客户端:个人 PC ” 和 “ 服务器 ” 就是两个节点。在 UML 中,使用一个立方体表示一个节点,节点名放在左上角。节点之间的连线表示系统之间进行交互的通信路径,在 UML 中被称为连接。通信类型则放在连接旁边的 “ 《》 ” 之间,表示所用的通信协议或网络类型。
(2)构件和接口。在部署图中,构件代表可执行的物理代码模块,如一个可执行程序。逻辑上它可以与类图中的包或类对应。例如,在图 17 中, “ 服务器 ” 结点中包含 “ QueryServer.exe ” 、 “ Find.exe ” 和 “ Query.dll ” 3个构件。
在面向对象方法中,类和构件等元素并不是所有的属性和操作都对外可见。它们对外提供了可见操作和属性,称之为类和构件的接口。界面可以表示为一头是小圆圈的直线。在图 17 中, “ Query.dll ” 构件提供了一个 “ 查询 ” 接口。