<软件工程>SE4-5 面向对象方法
本文最后更新于:2023年11月3日 下午
SE4 统一建模语言(UML)
在消化、吸收、提炼至今存在的所有软件建模语言的基础上,提出了 UML(统一建模语言)
UML 是软件设计与需求规约语言,是一种为面向对象系统的产品进行说明、可视化和编制文档的一种标准语言。
面向对象方法是一种以对象、对象关系等来构建软件系统模型的系统化方法。其认为一切系统都是由对象构成的。对象间的相互作用、相互影响,构成了各式各样的系统。面向对象不仅仅是一种程序开发方法,也是一种软件方法学
-
用 对象作为对事物的抽象表示,并以此作为系统的基本构成单位。
事物的静态特征用 属性表示,动态特征用对象的操作表示
对象的属性和操作合为一体,构成一个独立的实体,对外屏蔽其内部细节(对象)
-
对事物进行分类。具有相同属性和操作的分为一类。类是对象的抽象描述,每个对象是其所属类的实例(类)
-
通过在不同程度上运用抽象原则,能得到一般类和特殊类。特殊类继承一般类的属性和操作。
面向对象支持对这种继承关系的描述和实现,从而简化系统的构造过程及其文档(泛化)
-
复杂的对象可以用简单的对象作为其构成部分(细化)
-
对象间通过消息进行通信,以实现对象间的动态联系(依赖)
-
用 关联表达类之间的静态关系(关联)
UML 可用于可用于对象方法和构件方法,可用于所有应用领域和不同的实现平台。
UML 主要提供了两类术语:表达客观事物的术语、表达关系的术语
SE4.1 表达客观事物的术语
为了表达客观世界中各类事物,UML 给出了如下术语
SE4.1.1 类、对象
类(class):一组具有相同属性、操作、关系和语义的对象的描述。对象(object)是类的一个实例
classDiagram
class Class
Class:operation()
Class:attribute
Class:virtualOperation()*
类主要用于抽象客观世界中的事物,因此一般具有一组属性和操作
一个结构良好的类,应符合以下条件:
- 是对问题域或解域中事物的明确抽象
- 嵌入了小的、明确定义的责任集,并能将其很好地实现
- 清晰地分离了抽象的规约和实现
# 属性(attribute)
属性是类的一个命名属性,由该类的所有对象共享,用于表达对象状态的数据
一个属性往往具有所属的类型,用于描述该特性的实例能取值的范围
类的一个对象对每个属性有特定的值
一个类能有多个属性,也可以没有属性
定义属性的格式为([ ]
中的内容可选):[可见性]属性名[:类型][多重性][=初始值][{特性串}]
-
可见性:表明该属性是否可被其他类使用
值可以是:公有(+),受保护(#),私有(-),包内(~)
-
多重性:如
points[2..*]:Point
。可以不写,此时默认是 1…1 -
初始值
-
性质串:如
a:integer=1{frozen}
-
作用范围
# 操作(operation)
操作是一个类中所有对象要做的事情的抽象。一个类可以有复数个操作。操作的命名遵循驼峰法
表达操作的语法格式:[可见性]操作名[(参数表)[:返回类型]][{性质串}]
-
性质串:有以下几种
leaf:该操作是叶子操作
abstract:该操作是抽象操作
qucry:是完全没有副作用的纯函数。不会改变系统状态
sequential:一次只能调用对象的一个操作。相当于 synchronize
static:该操作没有隐式参数。
# 类的责任(responsibility)
在模型化一类事物中,其起始点往往是给出该类责任。即一个类承诺的任务(contract)或职责(obligation)
在实践中,每个结构良好的类至少要有一个职责
SE4.1.2 接口
接口是操作的一个集合。其中每个操作描述了类、构件或子系统的一个服务
接口通常有两种形式:
-
采用分栏和关键字 <<interface>> 的矩形符号表示
classDiagram class IClass{ <<Interface>> operation()* }
-
采用小圆圈和半圆圈表示
接口只能被其他类目使用,而不能访问其他类目。
接口不描述其中操作的实现,也没有属性和状态。接口在形式上等价于仅有抽象操作的抽象类。
接口间没有关联、泛化、实现和依赖,但能参与关联、泛化、实现和依赖
SE4.1.3 其他术语
除了 类和接口 外,还有 6 个术语
-
协作(Collaboration)
协作是一个交互,其涉及交互的三要素:交互各方、交互方式、交互内容
交互各方间的相互作用提供了某种协作性行为
在 系统/产品 建模中,可以通过协作来刻画一种由一组特定元素参与的、具有特定行为的结构。这组特定元素可以是给定的类或对象,因此,协作可以编 写 系统/产品 实现的一种构成模式
协作是系统体系结构的概念组块。其仅引用或使用在其他地方声明的类、接口、组件、节点和其他结构元素,而不能拥有自己的结构元素。
-
用况(Use Case)
也称用例。用况是对一组动作序列的描述,系统执行这些动作应产生对特定参与者有价值的、可观察的结果
在 系统/产品 建模中,用况一般用于模型化系统中的功能行为,是建立系统功能模型的一个重要术语。一个用况描述了系统的一个完整的功能需求
由用况表达的 系统/产品 功能,往往可以通过协作精化
-
主动类(Active Class)
主动类是一种至少具有一个进程或线程的类。主动类能够启动系统的控制活动,并且其对象行为通常是与其他元素行为并发的
在 系统/产品 建模中,主动类一般用于模型化系统中的并发行为
-
构件(Component)
系统中逻辑的且可替换的成分,通过外部接口隐藏了其实现
在一个系统中,具有共享的、相同接口的、相同逻辑行为构件是可以互相替代的。构件可以嵌套,大的构件可以嵌套小的构件
在 系统/产品 建模中,构件一般用于表达解空间中可以独立标识的部分。换言之,构件这一术语不能用于问题定义
-
制品(Artifact)
制品是系统中包含物理信息的、可替代的物理组件
在软件开发中,制品通常用于代表有关源代码信息或运行时信息的一个物理打包。在一个系统中,可能存在不同类型的部署制品,如源代码文件、可执行程序和脚本
-
节点(Node)
节点是运行时存在的物理元素,通常表示一种具有记忆能力和处理能力的计算机资源。一个构件可以驻留在一个节点中,也能从一个节点移动到另一节点
-
包(package)
为了组织类目,控制信息组织和文档组织的复杂性,UML 引入了包
包是模型元素的一个分组。一个包本身可以被嵌套在其他包中,并可以含有子包和其他种类的模型元素
包之间的关系分为两种:
-
访问依赖(Access)
目标包中具有可见性的内容增加到源包的私有命名空间中。源包能引用目标包内容,但不能输出之
-
引入依赖(Import)
目标包中有适当可见性的内容被加入到源包的公共命名空间中。这相当于源包对它们做了声明
-
SE4.1.4 常用的建模技术
-
模型化一个系统中的词汇
- 识别用于描述问题或解决方案的事物,将其表示为类
- 标识每个类的责任集,确保每个类的定义清晰,并使责任分配均衡
- 为每个类的责任的实现,提供所需属性和操作
-
模型化系统中的责任分布
目标是:均衡系统中每个类的责任,避免类过大或过小
- 为了完成某些行为,标识一组紧密协作的类。对这组中的每个类,标识其一组职责
- 将职责过多的类拆分为一些较小的抽象类。将职责琐碎的类合并为较大的类
- 考虑这些类的协作方式,调整其责任,避免类职责的过多或过少
-
模型化基本类型
在其他极端情况下,建模事物可能直接取自现实中解的编程语言。通常这些抽象包括简单类型,如数字、字符串、乃至自定义枚举类型。
对抽象为类型或枚举的事物建模时,可以用带有适当衍型的类表示
如需要详细描述与该类型相关的值域,可使用约束
SE4.2 表达关系的术语
为了表达事物间的相互依赖和作用,UML 给出了如下术语
SE4.2.1 关联(Association)
关联是类目间的一种结构关系,是对一组具有相同结构、相同链的描述。
链(link):对象间具有特定语义关系的抽象,实现后的链通常称为对象间的连接(connection)
关联两个类目的关联称为 二元关联。关联 n 个类名的关联称为 n 元关联
与类相比:
- 类是一组具有相同属性、操作、关系和语义的对象的描述
- 关联是一组具有相同结构和语义的链的描述
为了表达关联的语义,UML 采用了如下描述:
-
关联名:关联的标识,用于提示该关联的含义。为避免关联涵义上的歧义,可以给出关联方向
-
导航:对于一个给定类目,可以找到与之关联的另一类目。一般情况下,导航是双向的。也能限定导航为单向
-
角色:关联一端的类目对另一端的类目的一种呈现。当一个类目参与一个关联时,如果它具有一个特定角色,那么就要显式给出端点名,以此来命名该类目在关联中的角色
-
可见性:能通过导航找到另一类目的实例。有些情况下,需要限制关联外的实例通过该关联访问相关对象
-
多重性:类中对象参与一个关联的数目
-
限定符:限定符是一个关联的属性或属性表,这些属性的值将与该关联相关类的对象集做了一个划分
-
聚合:分类是增强客观实际问题语义的一种手段。满足 “一个类是另一类的一部分” 的关联就称为一个集合。集合是关联的特殊形式,表达 “整体/部分” 的关系
使用聚合这一术语时,应注意双方在概念上是否处于同一层次。如若是,则标识为一个部分类,否则应标识为属性
-
组合:组合是聚合的特殊形式。如果在一个时间段内,整体类的实例中至少包含一个部分类的实例,且该整体类负责创建和消除该部分类实例,那么这样的聚合称为组合
回想一下 Java 的 HashSet 和 HashMap 吧。回想完了?那忘掉吧,和这个没关系
-
关联类:一种具有关联和类特性的模型元素。一个关联类可以被看作关联,但也有类的特性。
-
约束:为进一步描述关联一端类的性质,UML 给出了 6 个约束
- 有序(ordered)
- 无重复对象(set)
- 有重复对象(bag)
- 有序集合(order set)
- 列表(list)或序列(sequence)
- 只读(readonly)
SE4.2.2 泛化
泛化是一般性类目(父类、超类)和其较为特殊性类目(子类)间的一种联系
如果两个类具有泛化关系,则:
- 子类能继承父类属性和操作,并可有更多的属性和操作
- 子类可以替换父类声明
- 子类同一操作的实现可能覆盖父类的实现,这种情况称为操作多态
- 可以在类目间创建泛化,如节点间、类和接口间等
无父类且拥有子类的类称为基类(根类),没有子类的类称为叶子类
具有单一父类的情况称为单继承,具有多个父类称为 人中赤兔 多继承。历史经验告诉我们,如果一个子类有多个父类,这些父类的结构或行为存在重叠,就可能产生一系列问题。因此,一般尽量采用单继承。
为进一步表达泛化语义,UML 提供了 4 个约束
- 完整(Complete):已在模型中给出了泛化的所有子类。尽管有所省略,也不允许添加新的子类
- 不完整(Incomplete):模型中未给出全部子类。因此,可以添加子类
- 互斥(Disjoint):父类对象最多允许该泛化中的一个子类作为其类型
- 重叠(Overlapping):父类对象可能具有泛化中多个子类作为其类型
SE4.2.3 细化
类目间的语义关系。其中一个类目规约了保证另一类目执行的契约
一般在以下地方使用细化
- 接口与其实现类或实现构件间
- 用况与其实现协作间
SE4.2.4 依赖
依赖是一种使用关系,用于描述一个类目使用另一类目的信息和服务
UML 对依赖进行了分类
-
绑定(Bind):源的实例化是使用目标给定的实际参数来达到的。
如:可以把模板容器类与该类中实例间的关系模型化为绑定。绑定涉及实参到形参的映射
-
导出(Derive):可以从目标推导出源。如:出生日期和年龄
-
允许(Permit):表明目标对源而言可见。一个类访问另一类的私有特征时,将这一关系模型化为允许
-
实例(Instanceof):源的对象是目标的一个实例
-
实例化(Instantiate):源的实例是由目标创建的
-
幂类型(Powertype):源是目标的幂类型
-
精化(Refine):源比目标更精细
-
使用(Use):表明源的公共部分的语义依赖于目标语义
按照 UML 观点,一切事物间关系都可用 依赖 来规约。关联、细化、泛化 都是一类特殊的依赖。
SE4.2.5 模型化关系
使用上述术语,能模型化下列关系
-
结构关系
系统中存在大量结构关系,可以用 关联 来模型化这种关系
对各种结构关系进行模型化时,可以采用两种方式
-
以数据驱动
对所标识的每个类,如果一个类要导航到另一个类的对象,就在这两个类间给出一个关联
-
以行为驱动
对所标识的每个类,如果一个类要与另一个类的对象交互,就在这两个类间给出一个关联
不论何种驱动方式,为了进一步给出关联语义,一般要做到如下两点:
- 给出关联的多重性
- 判断该关联是否为聚合或组合
-
-
继承关系
对于系统中存在的 一般/特殊 的关系,可以使用 泛化 对其进行规约
在对系统中的 一般/特殊 关系进行模型化时,应以共同的职责为驱动。发现一组类中具有的相同职责,继而抽取其共同职责及相关共同属性和操作,作为一般类,并表明该一般类与这组特殊类间的泛化关系
模型化继承关系时,应避免继承层次过深(多代子类)或过宽(多个父类)
-
精化关系
对于系统中存在的精化关系,可以使用 细化 对其进行规约
精化关系是两个不同抽象层间的一种关系。一个抽象层的一个事物通过另一抽象层的术语细化,并增加相应细节
软件分析和设计是一个不断求精的过程。经常使用该术语表达不同抽象层间的精化,以体现 “自顶向下,逐步求精” 的思想
在对系统中的精化关系进行模型化时,应以一个抽象层中的概念为驱动,使用下一抽象层的概念对其进行精化
-
依赖关系
人类在认识客观世界中,最常用的构造方法是:分类、整体/部分、一般/特殊。据此,在对系统中存在的各种关系进行模型化时,首先应考虑静态结构问题。不是结构关系、继承关系、精化关系时,才使用 依赖 对其进行规约。
一般只有在操作没有给出,或省略明显操作标记时,或模型还需描述与目标类的其他关系时,才将其关系模型化为依赖。如果给出了明显标记,一般不需要给出该依赖。
SE4.3 UML 的模型表达工具
graph TB
M(图)
M---S(结构图)
M---A(行为图)
S---CLASSDIAGRAM(类图)
S----OBJECTDIAGRAM(对象图)
S---STRUCTDIAGRAM(构件图)
S----PACKAGEDIAGRAM(包图)
S---DEPLOYDIAGRAM(部署图)
S----C_S_DIAGRAM(组合结构图)
A---USECASEDIAGRAM(用例图)
A----ACTIVITYDIAGRAM(活动图)
A---STATEDIAGRAM(状态图)
A----I(交互图)
I---SEQUENCEDIAGRAM(顺序图)
I----COMMUNICATIONDIAGRAM(通信图)
I---WTFDIAGRAM(交互概观图)
I----TIMINGDIAGRAM(定时图)
UML 为不同抽象层提供了 6 种可对 系统静态部分 建模的图形工具:
-
类图
类图显示了类(及接口)、类的内部结构及其他类的联系。类图是 OOAD 得到的最重要的模型。
-
构件图
在转入实现阶段前,可以用构件图表示如何组织构件。构件图描述了构件及构件间的依赖关系。
-
组合结构图
展示了类或协作的内部结构
-
对象图
展示了一组对象及它们间的关系。用对象图说明在类图中发现的事物实例的数据结构和静态快照。
-
部署图
部署图展示运行时进行处理的结点和在结点上生存的制品的配置。部署图用来对系统的静态部署视图建模
-
制品图
展示了一组制品及其间的依赖关系。利用制品图可以对系统的静态实现视图建模
UML 为不同抽象层提供了 7 种可对 系统动态部分 建模的图形工具:
-
用况图
需求模型
-
状态图
当对象的行为较为复杂时,可以用状态图作为辅助模型描述对象的状态及状态转移,从而更准确地定义对象的操作
-
活动图
注重从活动到活动的控制流,可用来描述对象的操作流程,也可以描述一组对象间的协作行为或用户的业务流程
-
顺序图
注重于消息的时间次序。可以用来表示一组对象间的交互情况
-
通信图
注重于收发消息的对象的组织结构。可以用来表示一组对象间的交互情况
-
交互概念图
用于描述系统的宏观行为。是活动图和顺序图的混合物
-
定时图
用于表示交互,它展现了消息跨越不同对象或角色的实际时间,而不仅仅关心消息的相对顺序
SE4.3.1 类图(Class Diagram)
类图通常包含:类、接口、类间关系。还能包含注解和约束,以及包或子系统。甚至可以包含一个实例,以便使其可视化
下面是一个类图的示例:
classDiagram
class Company {
}
class Department {
name Name
}
class Office {
address String
phone Number
}
class Headquarters {
}
class Person {
name Name
employeeID Integer
title String
getPhoto() Photo
getPhone() Number
getContactInformation() ContactInformation
getPersonalRecords() PersonalRecords
}
class ContactInformation {
address String
}
class PersonalRecords {
taxID
employmentHistory
salary
}
Company "1" *-- "1..*" Department
Company "1" *-- "1..*" Office
Office <|-- Headquarters : 泛化
Department --* Department : 0..1
Office "*"--"*" Department : Location
Department "*"--"1..*" Person : member
Department "*"--"1" Person : manager
Person ..> ContactInformation : 依赖
Person ..> PersonalRecords : 依赖
创建类图包括以下几方面工作:
-
对系统中的概念(词汇)建模,形成类图中的基本元素
使用 UML 的术语 “类”,来抽象系统中各个组成部分,包括系统环境。然后,确定每一类的责任,最终形成类图中的模型元素。
-
对待建系统中的各种关系建模,形成该系统的初始类图
使用 UML 中表达关系的术语来抽象系统中各成分间的关系,形成该系统的初始类图
-
当用 关联 关系建模时,是在对相互同等的两个类建模。给定两个类间的关联,则这两个类以某种方式相互依赖,且常常从两边都可以导航
-
对于每一对类,如果需要从一个类到另一类导航,就要在这两个类间建立一个关联
classDiagram class Person class Company Person --> Company : works for
-
对于每一对类,如果一个类的对象要与另一类的对象交互,并且后者不是前者的过程局部变量或操作参数,就要在这两个类间建立一个关联
-
如果关联中的一个类与另一端的类相比,前者在结构或组织上是一个整体,后者看来像是它的部分,则在靠近整体的一侧用一个菱形关联修饰,从而将其标记为聚合
classDiagram class Company class Department Company "1"o--"*" Department : 聚合
-
对于每一个关联,都要说明其多重性(否则,默认多重性为 *)
-
-
依赖 关系是使用关系。常见的依赖关系是两个类间的连接,一个类知识使用另一个类作为其操作参数
classDiagram class CourseSchdule { add(c:Course) remove(c:Course) } class Course CourseSchdule ..> Course
-
泛化 关系是 is-a-kind-of 关系。在对系统的词汇建模时,如果遇到结构或行为上与其他类相似的类,可以提取所有的共同的结构特征和行为特征,并把它们提升到较一般的类中。特殊类继承这些特征
classDiagram class Shape { origin: Point move(offset: Point) resize(ratio: Real) display() } class Circle { radius: Distance resize(ratio: Real) display() } class Polygon { vertexOffset: List of Point resize(ratio: Real) display() } class Rectangle { width: Distance height: Distance resize(ratio: Real) display() } Shape <|-- Circle : 泛化 Shape <|-- Polygon : 泛化 Shape <|-- Rectangle : 泛化
-
-
模型化系统中的协作,给出该系统的最终类图
使用类和 UML 中表达关系的术语,模型化一些类之间的协作,用类图对这组类及他们间的关系建模
-
对逻辑数据库模式建模(附加)
当需要给出数据库概念设计的指导,可以对要在数据库中存储的信息,采用类图对相应数据库模式中进行建模
SE4.3.2 用况图(Use Case Diagram)
用况图是表现一组 Use cases(用况、用例)、Actors(参与者) 以及它们间关系的图
(用况图_SE4_3_2……图片来源于网络!)
# 用况图的术语
-
主题
主题是一组用况描述的一个系统或子系统。这些用况描述了该主题的完整行为,而参与者则表示与该主题交互的另一种类
-
用况
使用视角:用况表达了参与者使用系统的一种方式
系统设计视角:一个用况规约了系统可以执行的一个动作序列,包括一些可能的变体,并对特定的操作者产生可见的、有值的结果
用例是系统分析和设计阶段的输入之一。是类、对象、操作的源,也是分析设计的一个依据。用例也是制定开发计划、测试计划、设计测试用例的依据之一。
用例能划分系统与外部实体的界限,是系统开发的起点
-
参与者
一组高内聚的角色。用户与用况交互时,该用户就是参与者
一个参与者一般可以表达与系统交互的那些人、硬件或其他系统的角色
参与者实际上不是软件应用的一部分,而是在应用环境之中。其实例代表以某种特定方式与系统进行交互
一个客体对象可以扮演多个参与者。一个参与者代表了客体一个方面的角色
-
关系
-
关联:参与关系。即操作者参与一个用例
关联是操作者和用例间的唯一关系
-
扩展:用例 A 到 用例 B 的扩展关系,指出了用例 B 的一个实例可以由 A 说明的行为予以扩展,并依据该扩展点定义的位置,A 说明的行为被插入到 B 中
-
包含:用例 A 到 用例 B 的一个包含,指出 A 的一个实例将包含 B 说明的行为,即这一行为将包含在 A 定义的那部分中
-
泛化:用例 A 到 用例 B 的泛化,指出 A 是 B 的特殊情况
扩展 和 包含 是 依赖 的变体。
-
# 用况图的使用
- 对系统语境建模
- 决定哪些行为是系统的一部分,哪些行为是外部实体执行的。以此标识系统边界,同时定义主题
- 在标识系统的参与者时,应考虑这些问题:谁需要得到系统帮助以完成其任务;谁执行系统的功能;系统与哪些硬件设备或其他系统交互;谁执行一些辅助功能进行系统的管理和维护
- 将一些相似的参与者组织为一般/特殊结构
- 在需要加深理解的地方,为每个参与者提供一个衍型
- 最后,将这些参与者放入用况图中,并建立它们与系统用况间的关联:通信路径
- 对系统需求建模
- 确定系统的基本用况。考虑每个参与者期望或需要的系统行为,将这些行为规约为相应用况
- 用况的结构化处理,即分解行为,形成必要的泛化结构和扩展、包含结构,并在用况图中给出各种已确定的关系
- 用况的语义表达。即通过注解和约束来修饰用况,特别是给出用况的一些非功能需求
SE4.3.3 顺序图(Sequence Diagram)
顺序图是一种交互图。由一组对象和对象间的关系组成。其中还包括对象间发送的消息
sequenceDiagram
participant c Client
participant Transaction
participant p ODBCProxy
activate c Client
c Client ->> Transaction : create()
c Client ->> Transaction : setActions(a, d, o)
activate Transaction
Transaction ->> p ODBCProxy : setValues(d, 3.4)
activate p ODBCProxy
deactivate p ODBCProxy
Transaction ->> p ODBCProxy : setValues(a, "CO")
activate p ODBCProxy
deactivate p ODBCProxy
Transaction -->> c Client : committed
deactivate Transaction
c Client -x Transaction : destory()
deactivate c Client
顺序图包含的内容:
- 交互的各方:角色或对象
- 交互方式:同步或异步
- 交互内容:消息
- 也能包含注解和约束
这些成分确定了交互的各种形态
从应用的角度来看,交互图是一个交互中各元素(各方、方式和内容)的投影。其中把这些元素的语义应用到交互图中
一些术语:
-
对象生命线
用于表示一个对象在一个特定时间段中的存在。对象生命线
被表示为垂直的虚线我上面的图里是垂直的实线。 -
消息
顺序图中包含了一些由时间定序的消息。消息表示为一条箭头线,由一条生命线到另一条生命线。
- 异步:
用枝形箭头线表示我上面是用叉号箭头表示 - 同步(调用):用实心三角箭头线表示。同步消息的回复用虚线表示
- 异步:
-
聚焦控制
聚焦控制用于表达一个对象执行一个动作的时间段。聚焦控制表现为细高的矩形。根据需要,也能嵌套使用聚焦控制
在顺序图中,也可以使用结构化控制操作符来实现更为复杂的流程
sequenceDiagram
participant user Person
participant bank ATM
loop invalid password
user Person ->> bank ATM : enter(password)
bank ATM ->> user Person : valid=verify(password)
end
opt valid password
par
user Person ->> bank ATM : enter(account)
user Person ->> bank ATM : enter(amount)
end
bank ATM ->> user Person : deliver cash
end
四种最常用的控制操作符(控制操作子):
-
选择执行操作符
该操作符记为:opt
该操作符由两部分组成:监护条件、监护体。监护条件是一个布尔表达式,可以出现在该监护体内任意一个生命线顶端的方括号内。
仅当进入该控制操作符,监护条件为真时,才执行监护体。
-
条件执行操作符
该操作符记为:alt
该操作符的体通过水平线分为几部分,每部分拥有一个监护条件,表示一个条件分支。可以存在一个 else 分支,表示其他分支都不为真时执行该分支。
仅当进入该控制操作符,并存在分支的监护条件为真时,才从那些为真的分支中选择一个执行。
多个分支条件为真时,选择的结果不确定。
-
并发执行操作符
该操作符记为:par
该操作符的体通过水平线分为几部分,每部分表示一个并行计算。多数情况下,每部分涉及不同生命线
当进入该控制操作符时,所有部分并发执行。
在每部分中的消息发送、接收是有次序的。但在所有并发中,消息的次序是任意的。
-
迭代执行
该操作符记为:loop
监护条件可以出现在该监护体内一个生命线顶端的方括号内。
只要在每次迭代前监护条件为真,该循环就会反复进行。当监护条件为假时,跳过该监护体
SE4.3.4 状态图(State Diagram)
状态图是显示一个状态机的图,其中强调了一种状态到另一种状态的控制流
状态机:一种行为,规约了一个对象在其生存期间因响应事件并作出响应而经历的状态
stateDiagram
[*] --> Idle
Idle --> [*] : shutDown
Idle --> Cooling : atTemp
Idle --> Heating : atTemp
state Heating {
[*] --> Activating
Activating --> Active
}
Cooling --> Idle : tooCold
Heating --> Cooling : tooHot
Cooling --> Heating : tooCold
Heating --> Idle : tooHot
状态图通常包含 简单状态和组合状态、事件、转换,也能包含注解和约束。
从使用角度来看,一个状态图可以包含一个状态机中任意的、所有的特征。即,状态图基本上是一个状态机中那些元素(分支、结合、动作状态、活动状态、对象、初始状态、最终状态、历史状态等)的一个投影
# 状态
一个状态是类目的一个实例(对象)在其生存周期间的一种条件或情况,该期间该对象满足这一条件,执行某一活动或等待某一消息
一个状态表达了一个对象所处的特定阶段,所具有的对外呈现及所能提供的服务。
UML 把状态分为三种:
-
初态:表达状态机默认的开始位置。用一个实心圆表示
-
终态:表达状态机的执行已经完成。用一个同心圆来表示
初态和终态都是伪状态。它们只有名字。从初态转移到正常状态可以给出一些特征,如监护条件和动作
-
正常状态:既不是初态又不是终态的状态
状态的规约:
-
进入/退出效应
是 进入/退出 该状态时执行的动作。为表达 进入/退出效应,UML 给出了 2 个专用动作符号:
- entry:标识在进入该状态时要执行的,由相应动作表达式所规定的动作。简称进入动作
- exit:标识在退出该状态时要执行的,由相应动作表达式所规定的动作。简称退出动作
一般情况下,进入/退出效应 不能有参数或监护条件。但位于类状态机顶层的进入效应可以有参数,以标识创建一个对象状态机时要接受的参数
-
状态内部转移
指没有导致该状态改变的内部转移。一般情况下,在此给出对象在这个状态中要执行的内部动作或活动列表。其中表达动作的一般格式是:
动作标号 / 动作表达式
动作标号标识了该环境下要调用动作表达式中的动作。动作表达式可以为空,此时斜线分隔符也能省略。
- do:为表达状态内部转换中的动作或活动,UML 给出了这个专用的动作符号。该符号标识正在进行由相应动作表达式规定的活动,并且只要对象在一个状态中没有完成由该动作表达式指定的活动,就一直执行之。当动作表达式完成时,可能产生一个完成事件。
动作标号 entry、exit、do 不能作为事件名
-
动作:动作是不可中断的原子计算。动作可以导致状态的改变或导致一个值的返回
-
活动:一个活动是指状态机中一种可中断的计算,中断处理后仍可继续。一个活动往往是由多个动作组成的。
-
子状态
如果在一个状态机中引入另一状态机,那么被引入的状态机被称为子状态机。没有子状态的状态被称为 简单状态,包含子状态的状态被称为 组合状态
子状态机分为:顺序子状态机(非正交)、并发子状态机(正交)
-
顺序子状态机(非正交)
关于子状态的进入(转入):
从组合外的状态可以进入该组合状态,那个源状态作为目标状态。此时,被嵌套的子状态一定有一个初态,以便进入组合状态并执行进入动作后,将控制传送给该初态。
也可以从组合外的状态转移到组合状态中的一个子状态,作为其目标状态。此时,进入组合状态并执行进入动作后,将控制传送给该子状态。
关于子状态的离开(转出):
可以从一个组合状态,或一个组合状态中的一个子状态离开。首先离开被嵌套状态,并执行那些被嵌套状态的退出动作。接着离开该组合状态,并执行组合状态的退出动作。
由此可见,一个转移的源是组合状态的场合,该转移的本质是中止被嵌套状态机的活动。
当控制到达该组合状态的子终态时,就触发一个活动完成的转移
如要指定在转移出该组合状态前所执行的那个活动所在的子状态,UML 引入了 浅历史状态 的概念,用于指明这样的子状态,并用 H 标识。
-
并发子状态机
正交子状态机是组合状态中一些并发执行的子状态。其中,用虚线段分成了多个正交区域,每个区域有各自的初态和终态
关于转入(分岔):
当一个转移到达具有多个正交区域的组合状态时,控制就被分成多个并发流(分岔)。正交区域的执行是并行的,相当于存在两个被嵌套的状态机
关于离开(汇合):
先达到终态的正交区域在其终态等待,直到所有正交区域都到达终态时,几个区域的控制汇合成一个控制流(汇合)。另外,当一个转移离开这样的组合状态时,控制汇成一个控制流。
-
-
被延迟事件
被延迟事件是在一个状态中不予处理的事件列表。往往需要一个队列机制,对这样的事件予以推迟并予排队,以便在该对象的另一状态中予以处理。
# 事件
一个事件是对一个有意义的发生的规约,该发生有其自己的时空。在状态机的语境下,一个事件是一个激励,可引发状态的转换
事件的种类:
-
内部事件
是在系统内对象间传送的事件。如:溢出异常
-
外部事件
在系统和参与者间传送的事件。如按下按钮,一个来自传感器的中断
在 UML 中可以模型 4 种事件:
-
信号
信号是消息的一个类目,是消息类型。信号是一种异步事件
信号有属性和操作,信号间可以有泛化
在 UML 中,可以将信号模型化为具有名字 <<singal>> 的衍型类。也可用衍型表示一个操作发送了一个特殊信号。也能在类的附加栏中为对象可能接受的、有名的信号进行模型化
-
调用
一个调用事件表示对象接受到一个操作调用的请求
可以使用在类的定义中的操作定义来规约调用事件。该事件或触发状态机中的一个状态转换,或调用目标对象的一个方法。
调用一般是同步事件,但也能将其规约为异步调用
-
时间事件和变化事件
时间事件是推移一段时间的事件。变化事件是一个条件得到满足,或表示状态的一个变化。
-
发送事件和接受事件
发送事件是类的一个实例发送一个调用事件或信号事件。接受事件是类的一个实例接受一个调用事件或信号事件
如果是一个同步调用事件,那么发送者和接受者都处在该操作执行期间的一个汇聚点上。即发送者的控制流一直被挂起,直到该操作执行完成
如果是一个信号事件,那么发送者和接受者并不汇合。即,发送者发出信号后不等待接受者响应。
以上两种情况下,事件可能丢失(如未定义对该事件的响应)。事件的丢失肯除法接受者的状态机(如有),或引起一个常规的方法调用
# 状态转换
状态转换是两个状态间的一种关系。
在第一个状态中的一个对象将执行一些确定的动作,当规约事件发生并且规约条件满足时,进入第二个状态。
状态转换的规约涉及以下几部分:
-
源状态
引发状态转换的那个状态
-
转换触发器
在源状态中由对象识别的事件。一旦满足监护条件,就让状态发生转换。其中,在同一个简单状态图中,如果触发多个转换,则高优先级优先。存在多个高优先级转换时,从中随机选择一个执行。
-
监护条件
一个布尔表达式。当某个转换触发器接受一个事件时,如果该表达式有值为真,则触发转换。如有值为假,并且此时没有其他可被触发的转换,则该事件丢失
-
效应
一种可执行的行为。例如可作用于对象上的一个动作,或间接地作用于其他对象的动作。但这些对象对那个对象是可见的
-
目标状态
转换完成后处于的状态
在 UML 中,把状态转换表示为从源状态出发,并在目标状态上终止的带箭头的实践
# 状态图的一般用法
- 是建立一个系统动态方面的模型。这些动态方面包括任意种类对象、任意系统结构(类、接口、构件、节点)视角下以事件定序的行为
- 建立一个场景的模型,其主要途径是针对用例图给出相应的状态图。
以上两种用法,一般都是对反应型对象的行为进行建模。为反应型对象建模时,步骤为:
- 选择状态机的语境,是类、用况或是子系统。
- 选择其实例的初始状态和最终状态,并分别给出初始状态和最终状态的前置条件和后置条件,以便指导以后的建模工作
- 标识某一可用的时间段,考虑该实例在此期间内存在的条件,以此判断该实例的其他状态。对此应以该实例的高层状态开始,继之再考虑这些状态的可能的子状态
- 判断该实例在整个生存周期内所有状态的有意义的偏序
- 判断可以触发该状态转换的事件。可以逐一从一个合理定序的状态到另一状态,来模型化其中的事件
- 为这些状态转移添加动作,或为这些状态添加动作
- 使用子状态、分支、合并和历史状态等,考虑简化状态机的方法
- 检查在某一组合事件下所有状态的可能性
- 检查是否存在死状态
- 跟踪整个状态机,检查是否符合期望的事件次序和响应
o
SE5 统一软件开发过程(RUP)
统一软件开发过程(Rational Unified Process)是对象管理组织推荐的一个有关过程的标准。是一种以用况驱动的,以体系结构为中心的迭代、增量式开发。
RUP 是基于 UML 的一种过程框架,较完整地定义了将用户需求转换成产品需要的活动集,并提供了活动指南及对产生相关文档的需求。
RUP 与 UML 是一对 “姐妹”,它们构成一种特定的软件开发方法学。可以认为:RUP + UML = 面向对象方法
(RUP阶段图_SE5)
-
初始阶段
获得与特定用况和平台无关的系统体系结构轮廓。位系统建立商业案例,确定项目边界,从业务角度指出该项目价值。
第一个重要里程碑:生命周期目标里程碑
-
细化阶段
目标是分析问题领域,建立健全体系结构基础,编制项目计划,淘汰项目中最高风险元素
第二个重要里程碑:生命周期结构里程碑
-
构造阶段
形成最终的系统体系结构基线,开发完整的系统,确保产品开始向客户交付
第三个重要里程碑:初始功能里程碑
-
交付阶段
确保软件对最终用户可用。基于用户反馈进行少量调整
第四个重要里程碑:产品发布里程碑
SE5.1 核心工作流
RUP 中有 9 个核心工作流。分为 6 个核心过程工作流,及 3 个核心支持工作流。
# 需求获取
RUP 运用用况来获取需求,得到系统 用况模型。
其基本步骤为:
-
列出候选需求
搜取特征。特征是一个新的项及其简要描述
-
理解系统语境
创建领域模型或业务模型。
业务用况模型:业务参与者和业务用况
业务对象模型:用交互图和活动图表达。包含:工作人员、业务实体、工作单元
-
捕获功能需求
建立系统的用况模型。发现和描述参与者、用况,确定用况优先级,精化用况,构造用户界面模型,用况模型结构化。
-
捕获非功能需求
# 需求分析
在系统用况模型基础上,创建系统 分析模型,以及该分析模型视角下的体系结构描述。
包含以下术语:
-
分析类:类的一种衍型,少有操作和特征标记,而用责任来定义其行为,且其属性和关系是概念性的
分为实体类、边界类、控制类
-
用况细化:一个协作。把一个用况用多个分析类的相互作用来细化。
-
分析包:把一些变化限制到一个业务过程、一个参与者行为或一组紧密相关的用况,形成一些分析包。分析包体现了局部化、问题分离的软件设计原理,体现了 “高内聚,低耦合”。
分析模型是由分析系统来定义的。分析系统包含一组有层次结构的包。
分析的主要活动:
-
体系结构分析
标识分析包,处理分析包之间的共性,标识服务包,定义分析包的依赖,标识重要的实体类,标识分析包和重要实体类的公共特征性需求
-
用况分析
标识分析类,描述分析类间的交互
-
类的分析
标识责任,标识属性,标识关联和聚合,包的分析
-
包的分析
确保分析包尽可能与其他包独立,确保分析包实现了其目标,描述依赖
用况模型 与 分析模型 的区别:
用况模型 | 分析模型 |
---|---|
使用客户语言描述 | 使用开发者语言描述 |
系统对外的视图 | 系统对内的视图 |
外部视角下的体系结构 | 内部视角下的体系结构 |
客户与开发者 “系统应做什么” 的契约 | 开发者理解系统如何设计实现的基础 |
需求间可能出现冗余、不一致 | 需求间不可能出现冗余、不一致 |
捕获系统功能 | 给出细化的系统功能 |
定义了需要进一步分析的用况 | 定义了用况模型中每一个用况的细化 |
# 软件设计
RUP 软件设计目标:定义满足系统/产品分析模型所规约需求的软件结构。给出 设计模型、部署模型。
相关术语:
- 设计类
- 用况细化
- 设计子系统
- 接口
设计模型、部署模型及相关视角下的体系结构描述:
- 设计模型:设计子系统、设计类、用况细化、接口
- 部署模型:一个对象模型,描述了系统的物理分布。即,如何把功能分布于各个节点上
软件设计的主要活动:
-
体系结构设计
标识节点和其网络配置,标识子系统和其接口,标识在体系结构方面有意义的设计类和其接口,标识一般性的设计机制
-
用况的设计
标识参与用况细化的设计类,标识参与用况细化的子系统和接口
-
类的设计
概括描述设计类,标识操作,标识属性,标识关联和聚合,标识泛化,描述方法,描述状态
-
子系统设计
维护子系统依赖,维护子系统提供的接口,维护子系统内容
设计模型:
- 设计子系统和服务子系统,及其依赖、接口和内容
- 设计类,及其具有的操作、属性、关系、实现需求
- 用况细化
- 设计模型视角下的体系结构描述
部署模型:
- 节点的特征及连接
- 主动类到节点的初始映射
分析模型 | 设计模型 |
---|---|
概念模型 | 软件模型 |
可用于不同设计 | 特定于一个实现 |
使用了三个衍生类:边界类、实体类、控制类 | 依赖实现的语言,使用了多个衍生类 |
几乎不是形式化的 | 是比较形式化的 |
开发费用少 | 开发费用多 |
结构层次少 | 结构层次多 |
动态的,很少关注定序方面 | 动态的,更多关注定序方面 |
概括给出系统设计 | 表明了系统设计 |
整个生命周期内不能修改 | 整个生命周期内应予维护 |
为构建系统定义一个结构,是基本输入 | 构建系统时,尽可能保留分析模型 |
# 实现与测试
实现的目标:基于设计类和子系统生成构件,对构件进行单元测试,进行集成和连接,并把可执行的构件映射到 部署模型。
实现的主要活动:
- 实现体系结构
- 集成系统
- 实现子系统
- 实现类
- 完成单元测试
测试包括:内部测试、中间测试、最终测试
测试的主要活动:
- 计划测试
- 设计测试
- 实现测试
- 执行集成测试
- 执行系统测试
- 评价测试