<软件工程>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 常用的建模技术

  1. 模型化一个系统中的词汇

    • 识别用于描述问题或解决方案的事物,将其表示为类
    • 标识每个类的责任集,确保每个类的定义清晰,并使责任分配均衡
    • 为每个类的责任的实现,提供所需属性和操作
  2. 模型化系统中的责任分布

    目标是:均衡系统中每个类的责任,避免类过大或过小

    • 为了完成某些行为,标识一组紧密协作的类。对这组中的每个类,标识其一组职责
    • 将职责过多的类拆分为一些较小的抽象类。将职责琐碎的类合并为较大的类
    • 考虑这些类的协作方式,调整其责任,避免类职责的过多或过少
  3. 模型化基本类型

    在其他极端情况下,建模事物可能直接取自现实中解的编程语言。通常这些抽象包括简单类型,如数字、字符串、乃至自定义枚举类型。

    对抽象为类型或枚举的事物建模时,可以用带有适当衍型的类表示

    如需要详细描述与该类型相关的值域,可使用约束

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 : 依赖

创建类图包括以下几方面工作:

  1. 对系统中的概念(词汇)建模,形成类图中的基本元素

    使用 UML 的术语 “类”,来抽象系统中各个组成部分,包括系统环境。然后,确定每一类的责任,最终形成类图中的模型元素。

  2. 对待建系统中的各种关系建模,形成该系统的初始类图

    使用 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 : 泛化
  3. 模型化系统中的协作,给出该系统的最终类图

    使用类和 UML 中表达关系的术语,模型化一些类之间的协作,用类图对这组类及他们间的关系建模

  4. 对逻辑数据库模式建模(附加)

    当需要给出数据库概念设计的指导,可以对要在数据库中存储的信息,采用类图对相应数据库模式中进行建模

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 的特殊情况

    扩展包含依赖 的变体。

# 用况图的使用

  1. 对系统语境建模
    • 决定哪些行为是系统的一部分,哪些行为是外部实体执行的。以此标识系统边界,同时定义主题
    • 在标识系统的参与者时,应考虑这些问题:谁需要得到系统帮助以完成其任务;谁执行系统的功能;系统与哪些硬件设备或其他系统交互;谁执行一些辅助功能进行系统的管理和维护
    • 将一些相似的参与者组织为一般/特殊结构
    • 在需要加深理解的地方,为每个参与者提供一个衍型
    • 最后,将这些参与者放入用况图中,并建立它们与系统用况间的关联:通信路径
  2. 对系统需求建模
    • 确定系统的基本用况。考虑每个参与者期望或需要的系统行为,将这些行为规约为相应用况
    • 用况的结构化处理,即分解行为,形成必要的泛化结构和扩展、包含结构,并在用况图中给出各种已确定的关系
    • 用况的语义表达。即通过注解和约束来修饰用况,特别是给出用况的一些非功能需求

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 中,把状态转换表示为从源状态出发,并在目标状态上终止的带箭头的实践

# 状态图的一般用法

  1. 是建立一个系统动态方面的模型。这些动态方面包括任意种类对象、任意系统结构(类、接口、构件、节点)视角下以事件定序的行为
  2. 建立一个场景的模型,其主要途径是针对用例图给出相应的状态图。

以上两种用法,一般都是对反应型对象的行为进行建模。为反应型对象建模时,步骤为:

  1. 选择状态机的语境,是类、用况或是子系统。
  2. 选择其实例的初始状态和最终状态,并分别给出初始状态和最终状态的前置条件和后置条件,以便指导以后的建模工作
  3. 标识某一可用的时间段,考虑该实例在此期间内存在的条件,以此判断该实例的其他状态。对此应以该实例的高层状态开始,继之再考虑这些状态的可能的子状态
  4. 判断该实例在整个生存周期内所有状态的有意义的偏序
  5. 判断可以触发该状态转换的事件。可以逐一从一个合理定序的状态到另一状态,来模型化其中的事件
  6. 为这些状态转移添加动作,或为这些状态添加动作
  7. 使用子状态、分支、合并和历史状态等,考虑简化状态机的方法
  8. 检查在某一组合事件下所有状态的可能性
  9. 检查是否存在死状态
  10. 跟踪整个状态机,检查是否符合期望的事件次序和响应
o

SE5 统一软件开发过程(RUP)

统一软件开发过程(Rational Unified Process)是对象管理组织推荐的一个有关过程的标准。是一种以用况驱动的,以体系结构为中心的迭代、增量式开发。

RUP 是基于 UML 的一种过程框架,较完整地定义了将用户需求转换成产品需要的活动集,并提供了活动指南及对产生相关文档的需求。

RUP 与 UML 是一对 “姐妹”,它们构成一种特定的软件开发方法学。可以认为:RUP + UML = 面向对象方法

(RUP阶段图_SE5)

  • 初始阶段

    获得与特定用况和平台无关的系统体系结构轮廓。位系统建立商业案例,确定项目边界,从业务角度指出该项目价值。

    第一个重要里程碑:生命周期目标里程碑

  • 细化阶段

    目标是分析问题领域,建立健全体系结构基础,编制项目计划,淘汰项目中最高风险元素

    第二个重要里程碑:生命周期结构里程碑

  • 构造阶段

    形成最终的系统体系结构基线,开发完整的系统,确保产品开始向客户交付

    第三个重要里程碑:初始功能里程碑

  • 交付阶段

    确保软件对最终用户可用。基于用户反馈进行少量调整

    第四个重要里程碑:产品发布里程碑

SE5.1 核心工作流

RUP 中有 9 个核心工作流。分为 6 个核心过程工作流,及 3 个核心支持工作流。

# 需求获取

RUP 运用用况来获取需求,得到系统 用况模型

其基本步骤为:

  • 列出候选需求

    搜取特征。特征是一个新的项及其简要描述

  • 理解系统语境

    创建领域模型或业务模型。

    业务用况模型:业务参与者和业务用况

    业务对象模型:用交互图和活动图表达。包含:工作人员、业务实体、工作单元

  • 捕获功能需求

    建立系统的用况模型。发现和描述参与者、用况,确定用况优先级,精化用况,构造用户界面模型,用况模型结构化。

  • 捕获非功能需求

# 需求分析

在系统用况模型基础上,创建系统 分析模型,以及该分析模型视角下的体系结构描述。

包含以下术语:

  • 分析类:类的一种衍型,少有操作和特征标记,而用责任来定义其行为,且其属性和关系是概念性的

    分为实体类、边界类、控制类

  • 用况细化:一个协作。把一个用况用多个分析类的相互作用来细化。

  • 分析包:把一些变化限制到一个业务过程、一个参与者行为或一组紧密相关的用况,形成一些分析包。分析包体现了局部化、问题分离的软件设计原理,体现了 “高内聚,低耦合”。

分析模型是由分析系统来定义的。分析系统包含一组有层次结构的包。

分析的主要活动:

  • 体系结构分析

    标识分析包,处理分析包之间的共性,标识服务包,定义分析包的依赖,标识重要的实体类,标识分析包和重要实体类的公共特征性需求

  • 用况分析

    标识分析类,描述分析类间的交互

  • 类的分析

    标识责任,标识属性,标识关联和聚合,包的分析

  • 包的分析

    确保分析包尽可能与其他包独立,确保分析包实现了其目标,描述依赖

用况模型 与 分析模型 的区别:

用况模型 分析模型
使用客户语言描述 使用开发者语言描述
系统对外的视图 系统对内的视图
外部视角下的体系结构 内部视角下的体系结构
客户与开发者 “系统应做什么” 的契约 开发者理解系统如何设计实现的基础
需求间可能出现冗余、不一致 需求间不可能出现冗余、不一致
捕获系统功能 给出细化的系统功能
定义了需要进一步分析的用况 定义了用况模型中每一个用况的细化

# 软件设计

RUP 软件设计目标:定义满足系统/产品分析模型所规约需求的软件结构。给出 设计模型部署模型

相关术语:

  • 设计类
  • 用况细化
  • 设计子系统
  • 接口

设计模型、部署模型及相关视角下的体系结构描述:

  • 设计模型:设计子系统、设计类、用况细化、接口
  • 部署模型:一个对象模型,描述了系统的物理分布。即,如何把功能分布于各个节点上

软件设计的主要活动:

  • 体系结构设计

    标识节点和其网络配置,标识子系统和其接口,标识在体系结构方面有意义的设计类和其接口,标识一般性的设计机制

  • 用况的设计

    标识参与用况细化的设计类,标识参与用况细化的子系统和接口

  • 类的设计

    概括描述设计类,标识操作,标识属性,标识关联和聚合,标识泛化,描述方法,描述状态

  • 子系统设计

    维护子系统依赖,维护子系统提供的接口,维护子系统内容

设计模型:

  • 设计子系统和服务子系统,及其依赖、接口和内容
  • 设计类,及其具有的操作、属性、关系、实现需求
  • 用况细化
  • 设计模型视角下的体系结构描述

部署模型:

  • 节点的特征及连接
  • 主动类到节点的初始映射
分析模型 设计模型
概念模型 软件模型
可用于不同设计 特定于一个实现
使用了三个衍生类:边界类、实体类、控制类 依赖实现的语言,使用了多个衍生类
几乎不是形式化的 是比较形式化的
开发费用少 开发费用多
结构层次少 结构层次多
动态的,很少关注定序方面 动态的,更多关注定序方面
概括给出系统设计 表明了系统设计
整个生命周期内不能修改 整个生命周期内应予维护
为构建系统定义一个结构,是基本输入 构建系统时,尽可能保留分析模型

# 实现与测试

实现的目标:基于设计类和子系统生成构件,对构件进行单元测试,进行集成和连接,并把可执行的构件映射到 部署模型

实现的主要活动:

  • 实现体系结构
  • 集成系统
  • 实现子系统
  • 实现类
  • 完成单元测试

测试包括:内部测试、中间测试、最终测试

测试的主要活动:

  • 计划测试
  • 设计测试
  • 实现测试
  • 执行集成测试
  • 执行系统测试
  • 评价测试

<软件工程>SE4-5 面向对象方法
https://i-melody.github.io/2023/11/03/软件工程/SE4 面向对象方法/
作者
Melody
发布于
2023年11月3日
许可协议