UML 类图

怪自己之前的 UML 没有注意到其重要性认真对待,现在学起 设计模式 简直了……
重新捡起来吧,为时不晚……

  • 类图(Class diagram)是显示了模型的静态结构,特别是模型中存在的类、类的内部结构以及它们与其他类的关系等。
  • 类图不显示暂时性信息

类的表示

  • 在UML中,类使用具有类名称、属性、操作分隔得长方形来表示。

eg. 定义一个类 Employee ,它包含属性 nameageemail,以及操作 modifyInfo(),在UML类图中该类如图示:

Employee

该类对应的Java代码如下:

1
2
3
4
5
6
7
8
public class Employee {
private String name;
private int age;
private String email;
public void modifyInfo() {
// ……
}
}
  1. 按照Java语言的命名规范,类名中每一个单词的首字母均大写。
  2. 属性表示方式:可见性 名称 : 类型 [ = 默认值]
  3. 操作表示方式:可见性 名称(参数列表) [ : 返回类型]
    • 可见性:+ 公有(public)、- 私有(private)、# 受保护(protected)、* 包内可见性(package)

类之间的关系

依赖关系

  • 依赖(Dependency)关系是一种使用关系【所以要尽量不使用双向的互相依赖】,特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系。
  • 在系统实施阶段,依赖关系通常通过三种方式来实现:
    • 第一种也是最常用的一种方式是将一个类的对象作为另一个类中方法的参数;
    • 第二种方式是在一个类的方法中将另一个类的对象作为其局部变量;
    • 第三种方式是在一个类的方法中调用另一个类的静态方法。
  • 大多数情况下,依赖关系体现在某个类的方法使用另一个类的对象作为参数【第一种方式】。

eg. 驾驶员开车,在 Driver 类的 drive() 方法中将 Car 类型的对象car 作为一个参数传递,以便在 drive() 方法中能够调用 carmove() 方法,且驾驶员的 drive() 方法依赖车的 move() 方法,因此类 Driver 依赖类 Car

1
2
3
4
5
6
7
8
9
10
11
12
public class Driver {
public void drive(Car car) {
car.move();
}
//……
}
public class Car {
public void move() {
//……
}
//……
}

关联关系

  • 关联关系(Association)是一种结构化关系,用于表示一类对象与另一类对象之间有联系。
  • 在实现关联关系时,通常将一个类的对象作为另一个类的成员变量。
  • 箭头及指向:带普通箭头的实心线,指向被拥有者。

单向关联

eg. 顾客(Customer)拥有地址(Address),则 Customer 类与 Address 类具有单向关联关系。

1
2
3
4
5
6
7
public class Customer {
private Address address;//定义为成员变量
//……
}
public class Address {
//……
}

双向关联

eg. 顾客(Customer)购买商品(Product)并拥有商品,反之,卖出的商品总有某个顾客与之相关联。因此,Customer 类和 Product 类之间具有双向关联关系。

此处输入图片的描述

1
2
3
4
5
6
7
8
public class Customer {
private Product[] products;
//……
}
public class Product {
private Customer customer;
//……
}

自关联

  • 在系统中可能会存在一些类的属性对象为该类本身,称为自关联。

eg. 一个节点类(Node)的成员又是节点对象。

1
2
3
4
public class Node {
private Node subNode;
//……
}

多重关联

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

eg. 一个界面(Form)可以拥有零个或多个按钮(Button),但是一个按钮只能属于一个界面,因此,一个 Form 类的对象可以与零个或多个 Button 类的对象相关联,但一个 Button 类的对象只能与一个 Form 类的对象关联。

1
2
3
4
5
6
7
public class Form {
private Button[] buttons; //定义一个集合对象
//……
}
public class Button {
//……
}

聚合关系

  • 聚合(Aggregation)关系表示整体与部分的关系
  • 在聚合关系中,成员对象是整体对象的一部分,但是成员对象可以脱离整体对象独立存在。
  • 聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。
  • 在代码实现聚合关系时,成员对象通常作为构造方法、Setter方法或业务方法的参数注入(Injection)到整体对象中。
  • 箭头及指向:带实心菱形的实线,菱形指向整体。

eg. 汽车发动机(Engine)是汽车(Car)的组成部分,但是汽车发动机可以独立存在,因此,汽车和发动机是聚合关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Car {
private Engine engine;
//构造注入
public Car(Engine engine) {
this.engine = engine;
}
//设值注入
public void setEngine(Engine engine) {
this.engine = engine;
}
//……
}
public class Engine {
//……
}

在上述代码中,Car 定义了一个 Engine 类型的成员变量,从语义上来说,EngineCar 的一部分,但是 Engine 对象可以脱离 Car 单独存在,因此在类 Car 中并不直接实例化 Engine,而是通过构造方法或者设值方法 Setter 将在类外部实例化好的 Engine 对象以参数形式传入到 Car 中,这种传入方式称为注入。正因为 CarEngine 的实例化时刻不同,因此它们之间不存在生命周期的制约关系,而仅仅只是整体与部分之间的关系而已。

组合关系

  • 组合(Composition)关系也表示类之间整体和部分的关系
  • 但组合关系中整体对象可以控制成员对象的生命周期:
    • 一旦整体对象不存在,成员对象也将不存在,成员对象与整体对象之间具有同生共死的关系。
  • 在代码实现组合关系时,通常在整体类的构造方法中直接实例化成员类。
  • 箭头及指向:带实心菱形的实线,菱形指向整体。

eg. 人的头(Head)与嘴巴(Mouth),嘴巴是头的组成部分之一,而且如果头没了,嘴巴也就没了,因此头和嘴巴是组合关系。

1
2
3
4
5
6
7
8
9
10
public class Head {
private Mouth mouth;
public Head() {
mouth = new Mouth(); //实例化成员类
}
//……
}
public class Mouth {
//……
}

泛化关系

  • 泛化(Generalization)关系也就是继承关系,用于描述父类与子类之间的关系。
  • 泛化关系指定了子类如何特化父类的所有特征和行为。
  • 在代码实现时,我们使用面向对象的继承机制来实现泛化关系:
    • 如在Java语言中使用 extends 关键字、在C++/C#中使用冒号 : 来实现。
  • 箭头指向:带三角箭头的实线,箭头指向父类。

eg. Student 类和 Teacher 类都是 Person 类的子类,Student 类和 Teacher 类继承了 Person 类的属性和方法,Person 类的属性包含姓名(name)和年龄(age),每一个 StudentTeacher 也都具有这两个属性,另外 Student 类增加了属性学号(studentNo),Teacher 类增加了属性教师编号(teacherNo),Person 类的方法包括行走 move() 和说话 say()Student 类和 Teacher 类继承了这两个方法,而且 Student 类还新增方法 study()Teacher 类还新增方法 teach()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//父类
public class Person {
protected String name;
protected int age;
public void move() {
//……
}
public void say() {
//……
}
}
//子类
public class Student extends Person {
private String studentNo;
public void study() {
//……
}
}
//子类
public class Teacher extends Person {
private String teacherNo;
public void teach() {
//……
}
}

实现关系

  • 实现关系(Realization)是一种类与接口的关系,表示类是接口所有特征和行为的实现。
  • 在接口中,通常没有属性,而且所有的操作都是抽象的,只有操作的声明,没有操作的实现。
  • 接口之间也可以有与类之间关系类似的继承关系和依赖关系,但是接口和类之间还存在一种实现关系:
    • 在这种关系中,类实现了接口,类中的操作实现了接口中所声明的操作。
  • 箭头指向:带三角箭头的虚线,箭头指向接口。

eg. 定义了一个交通工具接口 Vehicle,包含一个抽象操作 move(),在类 Ship 和类 Car 中都实现了该 move() 操作,不过具体的实现细节将会不一样,如图所示:

此处输入图片的描述

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Vehicle {
public void move();
}
public class Ship implements Vehicle {
public void move() {
//……
}
}
public class Car implements Vehicle {
public void move() {
//……
}
}

综合考虑

各种关系的强弱顺序:

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

下面这张UML图,比较形象地展示了各种类图关系

此处输入图片的描述

关于企鹅跟气候的关系

程杰出的《大话设计模式》P14页他这样解释:“你看企鹅和气候两个类,企鹅是很特别的鸟,会游不会飞。更重要的是,它与气候有很大的关联。我们不去讨论为什么北极没有企鹅,为什么它们要每年长途跋涉。总之,企鹅需要‘知道’气候的变化,需要‘了解’气候规律。当一个类‘知道’另一个类时,可以用关联(association)。关联关系用实线箭头来表示。”
根据引用的解释,再来看关联关系:是一种拥有的关系,它使一个类知道另一个类的属性和方法。就非常容易理解了。

参考资料

文章目录
  1. 1. 类的表示
  2. 2. 类之间的关系
    1. 2.1. 依赖关系
    2. 2.2. 关联关系
      1. 2.2.1. 单向关联
      2. 2.2.2. 双向关联
      3. 2.2.3. 自关联
      4. 2.2.4. 多重关联
    3. 2.3. 聚合关系
    4. 2.4. 组合关系
    5. 2.5. 泛化关系
    6. 2.6. 实现关系
  3. 3. 综合考虑
  4. 4. 参考资料

20160430-DesignPattern/

本页二维码