前言

在学习设计模式之前,需要了解一下什么是统一建模语言(Unified Modeling Language,UML),用人话来说,对于现实生活中的建筑大楼有它的设计图,而统一建模语言是程序的设计图,只有看懂了设计图,才会设计实现程序

UML

如果要盖一栋楼,为了让不把它盖成一个狗窝,所以需要先画一些设计图,这些设计图就是楼房的蓝图。设计图是一种设计语言,也就是模型语言,是不同工程设计人员和生产人员直接沟通的语言。

软件工程也需要使用模型来描述一个软件,使用户和开发人员都能够更好的理解开发的系统。建模是开发优秀软件的所有活动中最核心的部分之一,其目的是将结构和系统的行为联系起来,并对系统的结构进行可视化控制

随着软件系统的复杂度提高,好的建模语言需求越来越迫切,面向对象建模语言由此诞生,诞生初期软件建模增加到50多种,杂乱的建模语言让用户难以交流和使用

为了解决建模方法过多造成的种种问题,UML三位创始人,将所有建模语言统一起来也就形成了UML(统一建模语言)

UML的结构

UML是一种语言,也就意味着它有属于自己的表达规则。它不同于C#,JAVA等语言,而是一种分析设计语言,也就是一种建模语言,UML是由图形符号来达到建模语言,其主要结构如下:

视图

在UML建模过程中,使用不同的视图从不同的角度来描述软件系统。UML包括5种视图:

  1. 用户视图:以用户的观点表示系统的目标,它是所有视图的核心,该视图描述系统的需求
  2. 结构视图:表示系统的静态行为,描述系统的静态元素,如包,类与对象,以及它们之间的关系
  3. 行为视图:表示系统的动态行为,描述系统的组成元素(如对象)在系统运行时的交互关系
  4. 实现视图:表示系统中逻辑元素的分布,描述系统中物理文件以及它们之间的关系
  5. 环境视图:表示系统中物理元素的分布,描述系统中硬件设备以及它们之间的关系

在UML 2.0中,提供13种图,与上述5种视图相对应

  • 用例图(Use Case Diagram):又称为用况图,对应用户视图。在用例图中,使用用例来表示系统的功能需求,用例图用于表示多个外部执行者与系统用例之间以及用例与用例之间的关系。用例图与用例说明文档(Use Case Specification)是常用的需求建模工具,也称为用例建模
  • 类图(Class Diagram):对应于结构视图。类图使用类来描述系统的静态结构,类图包含类和它们之间的关系,它描述系统内所声明的类,但没有描述系统运行时类的行为。
  • 对象图(Object Diagram):对应于结构视图。对象图是类图在某个时刻的一个实例,用于表示对象实例之间的关系
  • 包图(Package Diagram):UML2.0新增图,对应于结构视图。包图用于描述包与包之间的关系,包是将多个元素组织在一起的机制,例如可以将多个类组织到一个包里
  • 组合结构图(Composite Structure Diagram):UML2.0新增图,对应于结构视图。组合结构图将每一个类放在一个整体中,从类的内部结构来审视一个类。组合结构图用于表示一个类的内部结构,用于描述一些包含复杂成员或内部类的类结构
  • 状态图(State Diagram):对应于行为视图。状态图用来描述一个特定对象的所有可能状态以及引起状态转移的事件。一个状态图包括一系列对象的状态以及状态和状态之间的转换
  • 活动图(Activity Diagram):对应于行为视图。活动图用来表示系统中的各种活动次序,它的应用比较广泛,既可以用来表示用例的工作流程,也可以描述类的某个方法的操作行为
  • 顺序图(Sequence Diagram):又称为时序图或者序列图,对应于行为视图。顺序图用于表示对象之间的交互,重点表示对象之间发送消息的时间顺序
  • 通信图(Communiccation):在UML 1.x中称为协作图,对应于行为视图。通信图展示了一组对象,这些对象间的连接以及它们之间收发的消息。它与顺序图是同结构图,也就是它们包含了相同的信息,只是表达形式不同,两这个可以相互转换
  • 定时图(Timing Diagram):UML 2.0的新增图,对应于行为视图。定时图采用一种带数字刻度的时间轴来精确表示消息的顺序,而不是像顺序图那样只是指定消息的相对顺序
  • 交互概览图
  • 组件图
  • 部署图

模型元素

在UML中,模型元素包括事物以及事物之间的联系。事物是UML的重要组成部分。事物和事物之间的联系就是对应的模型元素。同一个模型元素可以在不同的UML图中使用,但是不论在那个UML图中,它所表达的意思是不变的

通用机制

UML提供的通用机制为模型元素提供的额外的注释,修饰和语义等,主要包括规格说明,修饰,公共分类个扩展机制四种。扩展机制运行用户对UML进行扩展

类图

类图是使用频率最高的UML图之一,在设计模式中,我们将使用类图来描述一个模式的结构,通过类图来分析每一个模式实例

类与类图

类(Class)封装了数据和行为,是面向对象的重要组成部分,它是具有相同属性,操作,关系的对象集合的总称。在系统中,每个类具有一定的职责,职责指的是类所担任的任务,即类要完成什么样的功能,要承担什么样的义务。一个类可以有多种职责,设计的好的类一般只有一种职责。在定义类的时候,将类的职责分解成类的属性和操作(方法)。类的属性就即类的数据职责,类的方法即类的行为职责

在软件系统运行时,类将被实例化成对象(Object),对象对应于某个具体的事物。类是对一组具体相同属性,表现相同行为的对象的抽象,对象是类的实例(Instance)

类图(Class Diagram)通过出现在系统中的不同类来描述系统的静态结构,类图用来描述不同的类和它们之间的关系。在UML中。类使用具有类名称,属性,方法分隔的长方形来表示,其对应的代码为:

1
2
3
4
5
6
7
8
9
10
11
class Employee
{
private string name;
private int age;
private string email;

public void modifyInfo()
{

}
}

其对应的类图为:

image-20220621144756496

由上图可以看出,UML类图由三个部分组成:

  • 类名:,每个类都必须有一个名字,类名是一个字符串。按照C#类名规则,开头要大写字母

  • 属性(Attributes):属性是指类的性质,即类的成员变量。类可以有任意多个属性,也可以没有属性

    • UML规定属性的表示方法:

      1
      可见性 名称:类型 [默认值]
    1. 可见性表示该属性对类外的元素是否可见,包括公有(Public),私有(Private)和受保护(Protected)三种,在类图中对应的符号分别是“+”“-”和“#”表示.
    2. 名称表示属性名,用一个字符串表示,按照C#命名规则,一般是属性第一个字母要小写*
    3. 类型表示定义属性的数据类型,可以是基本属性类型,也可以用户自定义类型
    4. 默认值是一个可选项,即属性的初始值
  • 类的操作(方法)(Operations):操作是类的任意一个实例对象都可以使用的行为,操作是类的方法成员

    • UML规定操作的表示方法:

      1
      可见性 名称([参数列表]):[返回类型]
    1. 可见性的定义与属性定义相同
    2. 名称即操作名称或者方法名称,用一个字符串表示
    3. 参数列表表示操作的参数,其语法与属性表示相同,参数个数是任意的,多个参数之间用逗号隔开
    4. 返回类型是一个可选项,表示方法的返回值类型,依赖具体编程语言,可以是基本数据类型,也可以是用户自定义类型,也可以是空类型(void)。如果是构造方法,则无返回类型

类之间的关系

关联关系

关联关系是类与类之间最常用的一种关系,它是一种结构化的关系,用于表示一类对象与另一类对象之间有联系,例如:车辆与轮胎,老师与学生等。

在UML图中,通过实线来链接有关联的对象,在C#等编程语言中表示关联关系,通常将一个类的对象作为另一个类的属性在使用类图表示关联关系时,可以通过在关联线上标注角色名(字符串),一般使用一个表示两者之间的关系的动词作为角色名(字符串)。角色名不是必须的,在有必要的情况下标注,可以更好的理解关系。

对应的代码示例:

image-20220621153839727

对应的UML图示例:

image-20220621153910479

关联关系可以分为以下几种类型:

  • 双向关联

    默认情况下,关联是双向的,例如,顾客(Customer)购买商品(Product)并拥有商品;反之,卖出的商品总有某个顾客与之相关联。因此Customer类和Product类之间具有关联关系。

    其代码示例:

    image-20220621154104022

    其 UML 图示例:

    image-20220621154050982
  • 单向关联

    类的关联关系也可以是单向的,用带箭头的实线来表示,例如:顾客(Customer)拥有地址(Address)

    ,则Customer类和Address类是单向关系。

    其代码示例:

    image-20220621154141366

    其 UML 图示例:

    image-20220621154205119
  • 自关联

    在系统中可能存在一些自关联的类,类的属性对象类型是自身。例如:一个节点类(Node)的

    成员又是节点对象。

    其代码示例:

    image-20220621154230261

    其 UML 图示例:

    image-20220621154353368
  • 多重性关联

    多重性关联关系又称为重数性关联关系(Multiolicity),表示一个类的对象与另一个类的对象链接的个数。在UML重多重性关联关系可以直接在关联直线上增加一个数字表示与之对应的另一个对象的个数。

    表示方式 多重性说明
    1……1 表示另一个类的一个对象与一个该类对象有关系
    0……* 表示另一个类的一个对象与零个或者多个该类的对象有关系
    1……* 表示另一个类的一个对象与一个或多个该类的对象有关系
    0……1 表示另一个类的一个对象与零个或一个该类对象有关系
    m……n 表示另一个类的一个对象与最少m,最多n个该类的对象有关系(m<=n)

    例如:一个窗体可以拥有多个Button,但是一个Button只能对应一个窗体。代码示例:

    image-20220621161914228

    UML 图示例:

    image-20220621161929797

聚合关系

聚合关系(Aggregation)表示一个整体和部分的关系。通常在定义好一个整体类,从中分析整体类的组成结构,从而找出一些成员类,该整体类和成员类之间就形成了聚合关系。例如:计算机包括显示器,主机,键盘,鼠标等等。在聚合关系中,成员类是整体的一部分,即成员对象是整体对象的一部分,但是成员类的对象可以脱离整体独立存在。例如:汽车和发动机是聚合关系,但是发动机可以脱离汽车单独存在。

在 UML 图中,这种关系用带空心菱形的直线表示,代码示例:

image-20220621162325093

UML 图示例:

image-20220621162345390

在代码中,Car定义了一个Engine类的成员变量,从语义上来说,Engine是Car的一部分,但是Engine对象可以脱离Car独立存在。因此,在类Car中不直接实例化Engine,而是通过构造方法等将在类外部实例化好的Engine对象已参数形式传入Car中,这种方式称为:注入(Injection)。

组合关系

组合关系也表示整体和部分之间的关系,但是组合关系中整体和部分拥有统一的生存期。一旦

整体对象不存在时,部分对象也将不存在,部分对象和整体对象有共同生死的关系。例如:消息界面的按钮,控件等等和界面是组合关系,界面不存在,则控件部分也不存在。成员类是整体类的一部分,而整体类的生存期控制成员类的生存期。在UML中,这种关系用带实心菱形的直线表示。

例如:人的头(Head)与嘴巴(Mouth),嘴巴是头的部分,头不存在,则嘴巴不存在。UML 图示例:

image-20220621162508287

依赖关系

依赖关系(Dependency)是一种使用关系,特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系。大多数的情况下,依赖关系体现在某个类方法使用另一个类的对象作为参数。

在 UML 中,依赖关系用带箭头的虚线表示,由依赖方指向被依赖方。

例如:驾驶员开车,在Driver类的drive()方法中将Car类型的对象car作为参数传递给被依赖的一方,代码示例:

image-20220621162648222

UML 图示例:

image-20220621162705231

泛化关系

泛化关系(Generalization)也就是继承关系,也称为“is-a-kind-of”关系,泛化关系用于描述父类与子类之间的关系,父类又称作基类或超类,子类又称作派生类在UML中,泛化关系用带空心三角形的直线来表示。在代码实现时,使用面向对象的继承机制来实现泛化关系。

例如:Student类和Teacher类都是Person类的子类,Student类和Techer类继承了Person类的属性和方法,另外Student类增加了属性学号(studentNo),Teacher类增加了属性教师编号(teacherNo)

image-20220621162806974

接口与实现关系

现在很多面向对象编程,引入了接口的概念。在接口中,一般没有属性,而且所有操作都是抽象的,只有操作的声明,没有操作的实现。UML中用与类的表示法类似的方法表示接口。

接口之间也可以有与类之间关系类似的继承关系和依赖关系,但是接口和类之间还存在一种实现关系(Realiazation)。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的操作。

在UML中,类与接口之间的实现关系用带空心三角形的虚线来表示。

例如:定义了一个交通工具接口Vehicle,其中有一个抽象操作move(),在类Ship和类Car中都实现了该move()操作,不过具体的实现细节可能会不一样。

image-20220621162933693

顺序图

顺序图是最常用的系统动态建模工具之一,也是使用频率最高的交互图,它用于表示对象之间的动态交互,而且以图形化的方式描述了对象间消息传递的时间顺序。

顺序图的定义

顺序图(Sequence Diagram)是一种强调对象间消息传递次序的交互图,又称为时序图或者序列图。

顺序图以图形化的方式描述了在一个用例或者操作的执行过程中对象如何通过消息互相交互,说明了消息如何在对象之间发送和接收以及发送的顺序。顺序图允许直观地表示出对象的生存期,在生存期内,对象可以对输入消息做出响应,还可以发送消息。

顺序图组成元素与绘制

在UML中,顺序图将交互关系表示为一个二维图,纵向是时间轴,时间沿竖线向下延申;横向轴表示了在交互过程中的独立对象,对象的活动用生命线来表示。顺序图由执行者(Actor),生命线(Lifeline),对象(Object),激活(Activation)和消息(Message)等元素组成

UML顺序图的组成元素说明如下:

  1. 执行者是交互的发起人,使用与用例图一样的“小人”符号表示,在有些交互过程中无须使用执行者

  2. 生命线用一条纵向虚线表示

  3. 对象表示为一个矩形,其中对象名称标有下划线

  4. 激活是过程的执行,包括等待过程执行的时间。在顺序图中激活部分替换生命线,使用长条的矩形表示

  5. 消息是对象之间的通信,是两个对象之间的单路通信,是从发送者到接收者之间的控制信息流。消息在顺序图中由有标记的箭头表示,箭头从一个对象的生命线指向另一个对象的生命线。消息按时间顺序在途中从上到下排列。

  6. 一个复杂的顺序图可以划分为几个小块,每个小块称为一个交互片段(Interaction Fragment)。每个交互片段由一个大方框包围,在方框左上角的间隔区内标注该交互片段的操作类型,该操作类型用操作符表示,常用的操作符包括:

    • alt:多条路径,条件为真时执行
    • opt:任选,仅当条件为真时执行
    • par:并行,每一片段都并发执行
    • loop:循环,片段可以多次执行

例如:ATM的用户登录流程

image-20220621163052414

在顺序图中,有的消息对应于激活,表示它将会激活一个对象,这种消息被称为调用消息(Call Message);如果消息没有对应激活框,表示它不是一个调用消息,不会引发其他对象活动,这种消息称为发送消息(Send Message);如果对象的一个方法调用了自己的另一个方法时,消息是由对象发送给自身,这种消息被称为自身消息(Self Call Message)

顺序图中的消息还包括创建消息和销毁消息,创建消息用于使用New关键字创建另一个对象,而销毁消息用于调用对象的销毁方法将一个对象从内存中销毁。

image-20220621163130021

状态图

对于系统中那些具有多种状态的对象,状态图是一种常用的建模手段。状态图用于描述对象的各种状态以及状态之间的转换。在设计模式中,使用状态图来描述某些模式中对象的状态以及状态间的转换。

状态图的定义

状态图(Statechart Diagram)用来描述一个特定对象的所有可能状态以及引起其状态改变的事件。我们通常用状态图来描述单个对象的行为,它确定了由事件序列引出的状态序列,但并不是所有的类都需要使用状态图来描述它的行为,只有那些具有重要交互行为的类,我们才会使用状态图来描述。一个状态图包括一系列的状态以及状态之间的转移。

状态图适用于描述在不同用例之间的对象行为,但并不适合于描述包括若干协作的对象行为,因为一个状态图只能用于描述一个类的对象状态,如果涉及多个不同类的对象,则需要使用活动图。

状态图组成元素与绘制

在UML状态图中包括如下组成元素。

  1. 状态(State):又称为中间状态,用圆角矩形框表示。在一个状态图中可以有多个状态,每个状态包含两格:上格放置状态名称,下格说明处于该状态时对象可以进行的活动(Action)
  2. 初始状态(Initial State):又称为初态,用一个黑色的实心圆圈表示。在一个状态图中只能够有一个初始状态
  3. 结束状态(Final State):又称为终止状态或者终态,用一个实心圆外加一个圆圈表示。在一个状态图中可能有多个结束状态
  4. 转移(Transition):用于从一个状态到另一个状态之间的连线和箭头说明状态的转移情况,并用文字说明引发这个状态变换的相应事件是什么。事件有可能在特定的条件下发生,在UML中这样的条件称为守护条件(Guard Condition),发生事件时的处理也称为动作(Action)。状态之间的转移可带有标注,由三部分组成(每一个部分都可以省略),其语法为:事件名[条件]/动作名
image-20220621163228884 image-20220621163246622

End

终于弄完了最开始的设计模式部分,说实话我是没打算发这部分的,但是考虑到部分伙伴可能就没接触过设计模式还是把它腾上来了。后面的文章我会跳过后面设计原则说明部分,直接开始设计模式实际实现和相关内容。