UML 中的类图

介绍

类(Class)封装了数据和行为,是面向对象的重要组成部分,它是具有相同属性、操作、关系的对象集合的总称。类实例化成对象(Object),对象对应于某个具体的事物,是类的实例(Instance)。

在系统中,每个类都具有一定的职责,职责指的是类要完成什么样的功能,要承担什么样的义务。一个类可以有多种职责,设计得好的类通常有且仅有一种职责。在定义类的时候,将类的职责分解成为类的属性和操作(即方法):类的属性即类的数据职责,类的操作即类的行为职责。

类图(Class Diagram)使用出现在系统中的不同类来描述系统的静态结构,它用来描述不同的类以及它们之间的关系。

在 UML 类图中,类一般由三部分组成:

  1. 第一部分是类名:每个类都必须有一个名字,类名是一个字符串。
    按照 Java 语言的命名规范,类名中每一个单词的首字母均大写。
  2. 第二部分是类的属性(Attribute):属性是指类的性质,即类的成员变量。一个类可以有任意多个属性,也可以没有属性。
    按照 Java 语言的命名规范,属性名中的第一个单词全小写,之后每个单词首字母大写(驼峰命名法)。
    Java 中成员变量更准确的术语是 variable,又可以根据是否有 static 修饰符分为实例成员变量是( field,字段)和类成员变量是 (static field,类字段)。 Java 没有 attribute 这个术语,而是使用 property(属性) 特指那些通过 getter 和 setter 方法可以设置的属性。
    格式:[可见性] 名称:类型 [ = 默认值 ]
    image-20201217183458640
  3. 第三部分是类的操作(Operation):操作是类的任意一个实例对象都拥有的行为,是类的成员方法。
    按照 Java 语言的命名规范,方法名中的第一个单词全小写,之后每个单词首字母大写。
    Java 中成员方法更准确的术语是 method,又可以再细分为实例方法(instance method)和类方法(class method),除非用 static 显示指定,否则默认是是实例方法。
    格式:[可见性] 名称([参数列表]) [:返回类型 ]
    image-20201217183526275

UML 类图中可以给属性或方法加上下划线表示 static 。属性和操作前面的可选的四种可见性修饰符,可见范围逐渐变大:

  • -:private,类访问权限,表明只能只能在当前类中被访问。
  • ~ :package/default,包访问权限,表明只能在当前类或当前包中的其他类被访问。
  • #:protected,子类访问权限,表明只能在当前类、当前包、该类的子类中被访问。
  • +:public,公开访问权限, 表明可以在任意地方被访问。

依赖关系

依赖(Dependency)关系是一种使用(use-a)关系,特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系。

依赖通常指的是编译时依赖而不是运行时依赖。A 依赖 B 简单理解就是类 A 使用到了类 B,A 编译的时候 B 必须要存在。大多数情况下,依赖关系体现在某个类的方法使用另一个类的对象作为参数。这种依赖具有偶然性、临时性,可能传入的参数一旦变化这种依赖就不存在了。

在 UML 中,依赖关系用带箭头的虚线表示,由依赖的一方指向被依赖的一方。依赖关系通常是单向的,但也可以存在双向的相互依赖。
在 Java 中,依赖可以表现为:方法中的形参、方法中的局部变量、静态方法调用。

image-20201217170924215

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Driver {
public void drive(Car car) //方法中的形参:将一个类的对象作为另一个类中方法的参数
{
car.move();
Car c ; //方法中的局部变量:在一个类的方法中将另一个类的对象作为其局部变量
Car.getInstace(); //静态方法调用:在一个类的方法中调用另一个类的静态方法
}
……
}
public class Car {
public static Car getInstance() {
return new Car();
}
public void move() {
......
}
……
}

关联关系

关联

一个类引入其他类的对象作为成员变量,代表这个类可以知道另外一个类的属性和方法。关联关系体现的是两个类,或者类与接口之间的强依赖关系,这种关系很强烈,比依赖更强,不是偶然性的,也不是临时性的,而是一种长期性,相对平等的关系,类定义完成后这种关系就不可改变了。普通关联的两个类之间并没有明显的整体与部分的关系。

关联(Association)关系是类与类之间最常用的一种关系,它是一种结构化关系,用于表示一类对象与另一类对象之间有联系。

在 UML 类图中,用实线连接有关联的对象所对应的类,带箭头的实线表示单向关联,双向箭头或者没有箭头的实线表示双向关联。在使用类图表示关联关系时可以在关联线上标注角色名,角色关系常作为实例变量的标识符。

在 Java 中,一个类持有另一个类的对象实例作为成员变量表示关联关系。

单向关联:类的关联关系也可以是单向的,单向关联用带箭头的实线表示。
image-20201217170225262

1
2
3
4
5
6
7
8
public class Customer {
private Address address;
……
}

public class Address {
……
}

双向关联:默认情况下,关联是双向的。
image-20201217170159625

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

public class Product{
private Customer customer;
……
}

自关联:在系统中可能会存在一些类的属性对象类型为该类本身,这种特殊的关联关系称为自关联。表示链式结构时经常用到。
image-20201217170310273

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

多重性关联 :多重性关联关系又称为重数性(Multiplicity)关联关系,表示两个关联对象在数量上的对应关系。在 UML 中,对象之间的多重性可以直接在关联直线上用一个数字或一个数字范围表示。

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

image-20201217170535792

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

}

聚合

聚合(Aggregation)关系是一种特殊的关联关系,体现的是一种 has-a 关系,聚合关系的两个类的对象之间存在整体与部分的关系,并且成员对象可以脱离整体对象独立存在,他们具有各自的生命周期,部分可以属于多个对象,也可以被多个对象共享。

在 UML 中,聚合关系用带空心菱形的直线表示。
image-20201217170642490

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 {
……
}

组合

组合(Composition)关系也是关联关系的一种特例,体现的是一种 contain-a 关系,比聚合更强,是一种强聚合关系。组合关系的两个类的对象之间也存在整体与部分的关系,但此时整体与部分是不可分的,部分不能脱离整体单独存在。组合关系中整体对象可以控制成员对象的生命周期,一旦整体对象不存在,成员对象也将不存在,成员对象与整体对象之间具有同生共死的关系。

在 UML 中,组合关系用带实心菱形的直线表示。
image-20201217170849349

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

public class Mouth {
……
}

注意:(普通)关联、聚合、组合这三种关系在代码层面的表现一致的,只能从类所代表的现实意义来区分。

泛化关系

泛化(继承)

泛化可以复用已有的类,为类增加新功能。例如 A 是 B 和 C 的父类,B,C 具有公共类(父类)A。称 A 是 B 和 C 的一般化(概括,也称泛化),或者称 B 和 C 是 A 的特化(Specialization )。

泛化(Generalization)关系使用面向对象中的继承(Inheritance)机制实现,用于描述父类与子类(或子接口和父接口)之间的 is-a 关系。父类(Parent class)又称为基类(Base class)或超类(Superclass),子类(Child class,或 Subclass)又称为派生类(Derived classes)。

在 UML 中,泛化关系用带空心三角形的直线来表示。父类中出现的属性和操作不需要在子类的类图中重复标出,除非要覆盖父类中的属性和操作。
在 Java 中通过关键字 extends 来表示。

image-20201217171035545

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//父类
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() {
……
}
}

实现

接口之间也可以有与类之间关系类似的继承关系和依赖关系,但是接口和类之间还存在一种实现(Realization / Implementation)关系,可以理解为一种特殊的继承——被继承的父类是接口(完全的抽象类)。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的操作。接口是对行为的抽象,表示具备了某种能力(can-do),实现是类与接口中最常见的关系。

在 UML 中,类与接口之间的实现关系用带空心三角形的虚线来表示。
在 Java 中通过 implements 关键字来表示,Java 中的类不能同时继承多个类但是可以同时实现多个接口。

image-20201217171143240

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Vehicle {
public void move();
}

public class Ship implements Vehicle {
public void move() {
……
}
}

public class Car implements Vehicle {
public void move() {
……
}
}

参考

  1. 刘伟:《Java 设计模式》
  2. Mermaid 官方文档
  3. Java 术语表

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!