我们有时候在看别人的源码的时候,或者去理解一个设计模式,经常要看类图。软件开发者通过 UML 沟通对象的设计,而对于简单任务来讲又很少画它,所以在此记录下来,方便查看。

内容概览

类图用于描述系统中所包含的类以及它们之间的关系,帮助人们简化对系统的理解,它是系统分析和设计阶段的重要产物,也是系统编码和测试的重要模型依据。

类封装了数据和行为,是面向对象的重要组成部分,他是具有相同属性、操作、关系的对象的集合。一个类可以有多种职责,但设计好的类应该具备 单一职责。类通常可以分为 3 种:实体类(Entity Class)、控制类(Control Class)、边界类(Boundary Class)

  • 实体类:对应系统中的每个实体,即所说的 Domain,实体类既包括存储和传输数据的类,也包括操作数据的类,一般使用数据库的表或文件来存储。如用户类、商品类,由此可以看出实体类来源于需求说明的中的名词。
  • 控制类:控制类一般体现业务逻辑,提供相应的操作,即通常所说的 Service,将控制类抽象出来可以实现数据库与呈现层之间的解耦。如用户注册类、商品管理类,由此可以看出控制类一般是个动宾结构转换来的名词。
  • 边界类:对应系统中外部用户交互的对象,即所说的 View/View Model,其实就是呈现层如界面类、菜单类、窗体类。

在面向对象分析和设计的初级阶段,通常先识别出实体类,绘制初始类图,此时的类图也被称为领域模型,包括实体类以及它们之间的关系。

类的 UML 图示

在 UML 中,类使用包含类名、属性和操作且带有分隔线的长方形来表示,如定义一个 Employee 类,它包含属性 name、age、ID以及操作 ChangeDepartment(),在 UML 类图中如下表示:

Class diagram

对应的 C# 代码如下:

public class Employee
{
        private string Name;
        private int Age;
        private string ID;

        public void ChangeDepartment()
        {

        }
}

看到图中字段和方法前有不同的表示符号,代表的是可见性,包括公有(public)、私有(private)、受保护(protected) 3种,plant uml分别用 +、-、# 代表。

类与类之间的关系

泛化(继承)

泛化关系用一条带箭头的实线表示两个类之间是一种继承关系,如下图:

继承关系

员工和管理人员继承了用户类的属性和方法。

实现

实现关系表示的是类与接口的关系,类实现的接口,如下图:

实现关系

关联

关联是类与类之间常用的一种关系,代码中通常将一个类的对象作为另一个类的成员变量。

1.双向关联

如下图所示顾客和商品之间的关系:

双向关联

namespace ClassDiagram
{
    public class Customer
    {
        private Product[] products;
    }

    public class Product
    {
        private Customer customer;
    }
}

2.单向关联

如下图所示顾客和地址之间的关系:

单向关联

namespace ClassDiagram
{
    public class Customer
    {
        private Address address;
    }

    public class Address
    {
    }
}

3.自关联

树节点之间就是自关联关系如下图表示:

自关联

public class Node
{
    private Node subNode;
}

4.多重性关联

多重性关联表示两个关联对象在数量上的对应关系,如一个界面Form可以拥有0个或多个按钮,下图表示:

多重性关联

public class Form
{
    private Button[] buttons;
}

public class Button
{

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

聚合

聚合关系表示整体与部分的关系,在聚合关系中,成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而存在。 在 UML 中,聚合关系用带空心的菱形表示,如:轮胎是汽车的组成部分,但是轮胎可以脱离汽车而单独存在。

聚合


namespace ClassDiagram
{
    public class Car
    {
        private Wheel wheel;

        #构造注入
        public Car(Wheel wheel)
        {
            this.wheel = wheel;
        }

        #设值注入
        public void SetWheel(Wheel wheel)
        {
            this.wheel = wheel;
        }
    }

    public class Wheel
    {        
    }
}

组合

组合关系和聚合很像,也是类整体与部分的关系,但是在组合关系中整体对象可以控制成员对象的生命周期,一旦整体对象不存在,成员对象也就不复存在,他们之间是“不求同年同月同日生,但求同年同月同日死的”结拜兄弟关系。组合关系在 UML 中带实心的菱形表示,如头和眼睛就是组合关系:眼睛是头的一部分,头没了,眼睛也就死翘翘了。

组合

namespace ClassDiagram
{
    public class Head
    {
        private Eye eye;
        public Head()
        {
            this.eye = new Eye();
        }
    }

    public class Eye
    {

    }
}

依赖

最后再来说说依赖关系,也就是一个类与另一个类是调用关系,即一个类的实现需要另外一个类的协助,实际代码中我们应该避免互相依赖。一般来说,被依赖的对象的往往是以局部变量、方法参数的形式存在于对象中,与关联关系不同,它不会以成员变量的形式存在于依赖对象中,每一个依赖都有一个名称。依赖关系用带箭头的虚线表示,箭头指向被依赖的对象,如下图:

依赖

namespace DesignPattens
{
    public class B
    {
        public string OutputString()
        {
            return "foo";
        }
    }

    public class A
    {
        public void Call(B b)
        {
            var bar = b.OutputString();
            //Other opearations
        }
    }
}

各种关系的强弱关系

泛化==实现>组合>聚合>关联>依赖

  • 聚合:天下没有不散的宴席,聚散离合都无所谓,所以用空心的菱形,虚的很!
  • 组合:我忘你亡,共生共死,是一根绳子上的蚂蚱,所以用实心的菱形,实打实嘛