UML 类图详解

下文中的示例代码可能不符合实际开发情况,仅作为 UML 类图演示。


引言

UML 类图是使用最频繁的 UML 图之一。

类图是面向对象系统建模中最常用和最重要的图,是定义其它图的基础。类图主要是用来显示系统中的类、接口以及它们之间的静态结构和关系的一种静态模型。类图中最基本的元素是类、接口。软件设计师设计出类图后,程序员就可以用代码实现类图中包含的内容。


类、接口和包的表示方法

类(具体类和抽象类)在类图中用矩形框表示,矩形框分为三层:第一层是类名字;第二层是类的成员变量;第三层是类的方法。

更多具体规范可以查看:What is Class Diagram? (visual-paradigm.com)

UML 规定类图中属性的表示方式为:

1
可见性 名称 : 类型 [ = 缺省值]

类图中的方法可以表示为:

1
可见性 方法名 (参数名 : 参数类型) : 返回值类型

其中,方法的多个参数间用逗号隔开,无返回值时,其类型为 void

成员变量以及方法前的访问修饰符用符号来表示(即表示方法中的可见性部分):

  • -:表示 private
  • #:表示 protected
  • ~:表示 default,也就是包权限
  • _:表示 static
  • +:表示 public
  • 斜体:表示 abstract抽象类的类名以及抽象方法的名字都需要用斜体字表示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person {
public String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() { return name; }

public int getAge() { return age; }

private void setName(String name) { this.name = name; }
}

接口

接口在类图中也是用矩形框表示,但是与类的表示法不同的是,接口在类图中的第一层顶端用构造型 «interface» 表示,下面是接口的名字,第二层是方法。

接口中方法和类中方法的表示形式一样。

1
2
3
4
public interface Shape {
public float getArea();
public float getCircumference()
}

类和接口一般都出现在包中,UML 类图中包的表示形式如下图所示。

1
2
3
4
5
package AAA;

public class BBB {
// ...
}

类图中的关系

UML 类图中共有 6 种关系,如下图所示:

UML-类图关系

实现关系 Realization

很多面向对象编程语言(如 Java)中都引入了接口的概念。实现关系是指接口及其实现类之间的关系。在 Java 代码中,实现关系可以直接翻译为关键字 implements

在 UML 类图中,实现关系用空心三角和虚线组成的箭头来表示,箭头指向方法为从实现类指向接口。

1
2
3
4
5
public interface FileSaveable {
}

public class File implements FileSavealbe {
}

泛化关系 Generalization

泛化关系,用于描述父类与子类之间的关系,其中父类又称作超类,子类又称为派生类。注意,父类和子类都可为抽象类或者具体类。

Java 代码中,对象之间的泛化关系可以直接翻译为关键字 extends

在 UML 类图中泛化关系使用一条实线空心箭头由子类指向父类。

1
2
3
4
5
public class Animal {
}

public class Dog extends Animal {
}

关联关系 Association

关联关系是指对象和对象之间的连接,它使一个对象知道另一个对象的属性和方法。在 Java 中实现关联关系时,通常将一个类的对象作为另一个类的成员变量。

在 UML 类图中,用实线连接有关联关系的类,并可在关联线上标注角色名或关系名。

关联关系有双向关联、单向关联、自关联和多重性关联四种,下面将分别介绍。

双向关联

如果两个对象都知道(即可以调用)对方的公共属性和操作,那么二者就是双向关联。

在 UML 图中,双向关联关系用带双箭头的实线或者无箭头的实线表示。

1
2
3
4
5
6
7
public class DatabaseAdmin {
private Database database;
}

public class Database {
private DatabaseAdmin admin;
}

单向关联

如果只有一个对象知道(即可以调用)另一个对象的公共属性和操作,那么就是单向关联。大多数关联都是单向关联,单向关联关系更容易建立和维护,有助于寻找可重用的类。

在 UML 图中,单向关联用一个带箭头的实线表示,箭头指向被关联的对象。

1
2
3
4
5
6
7
8
public class Employee {
private BaseInfo info;
}

public class BaseInfo {
private String name;
private int age;
}

自关联

自关联指属性类型为该类本身。例如在链表中,每个节点持有下一个节点的实例。

1
2
3
4
public class Node {
private Node next;
private int value;
}

多重性关联

一个对象可以持有其它对象的数组或者集合。多重性关联关系,表示两个对象在数量上的对应关系。

在 UML 类图中,通过放置多重性表达式在关联线的末端来表示。多重性表达式可以是一个数字、一段范围或者是它们的组合。常见的多重性表达式如下所示。

表达式 含义
m(数字) 另一个类的一个对象与该类的 m 个对象有关系
0..* 另一个类的一个对象只与该类的零个或多个对象有关系
0..1 另一个类的一个对象与该类的对象没关系或者只与该类的一个对象有关系
1..* 另一个类的一个对象与该类的一个或多个对象有关系
m..n 另一个类的一个对象与该类最少 m,最多 n 个对象有关系
1
2
3
4
5
6
7
public class Employee {
private Task[] tasks;
}

public class Task {
private int id;
}

依赖关系 Dependency

依赖(Dependency)关系是一种弱关联关系。如果对象 A 用到对象 B,但是和 B 的关系不是太明显的时候,就可以把这种关系看作是依赖关系。如果对象 A 依赖于对象 B,则 A “use a” B。

在 UML 类图中,依赖关系用一个带虚线的箭头表示,由使用方指向被使用方,表示使用方对象持有被使用方对象的引用。

Java 代码中,依赖关系的表现形式体现为以下的 4 个方面:

  1. B 类的实例作为 A 类构造器或方法的参数

    1
    2
    3
    4
    5
    6
    // A : class Driver
    // B : class Car
    public class Driver {
    public drive(Car car) {
    }
    }
  2. B 类的实例作为 A 类构造器或方法的局部变量

    1
    2
    3
    4
    5
    6
    7
    // A : class Driver
    // B : class Car
    public class Driver {
    public method1() {
    Car car = new Car();
    }
    }
  3. B 类的实例作为 A 类方法的返回值

    1
    2
    3
    4
    5
    6
    7
    // A : class Driver
    // B : class Car
    public class Driver {
    public method2() {
    return new Car();
    }
    }
  4. A 类调用 B 类的静态方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // B : 被使用方
    public class B {
    public static void method1() {}
    }

    // A : 使用方
    public class A {
    public void method2() {
    B.method1();
    }
    }

B 类的实例作为 A 类构造器或方法的参数的示例

1
2
3
4
5
6
// A : class Driver
// B : class Car
public class Driver {
public drive(Car car) {
}
}

聚合关系 Aggregation

聚合是关联关系的一种特例,它体现的是整体与部分的拥有关系,即 “has a” 的关系。此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享,所以聚合关系也常称为共享关系。

从代码实现上来讲,部分对象不由整体对象创建,一般通过整体类的带参构造方法或者 Setter 方法或者其它业务方法传入到整体对象,并且有整体对象以外的对象持有部分对象的引用。

在 UML 类图中,聚合关系用空心菱形加实线箭头表示,空心菱形在整体一方,箭头指向部分一方。

1
2
3
4
5
6
7
8
9
public class Department {
private Employee employee;
public void setEmployee(Employee e) {
this.employee = e;
}
}

public class Employee {
}

设计模式装饰模式中,装饰类的对象与被装饰类的对象即为聚合关系。

组合关系 Composition

组合也是关联关系的一种特例,它同样体现整体与部分间的包含关系,即 “contains a” 的关系。但此时整体与部分是不可分的,部分也不能给其它整体共享,作为整体的对象负责部分的对象的生命周期,一旦整体对象不存在了,成员对象也即随之消亡。这种关系比聚合更强,也称为强聚合。

从代码实现上看,一般在整体类的构造方法中直接实例化成员类,并且除整体类对象外,其它类的对象无法获取该对象的引用

在 UML 图中,组合关系用实心菱形加实线箭头表示,实心菱形在整体一方,箭头指向部分一方。

1
2
3
4
5
6
public class A {
private B b = new B();
}

public class B {
}

设计模式代理模式中,代理类的对象与被代理类的对象即为组合关系。


易混淆解析

依赖关系与关联关系的区别是?

依赖关系是较弱的关系,一般表现为在局部变量中使用被依赖类的对象、以被依赖类的对象作为方法参数或返回值以及使用被依赖类的静态方法。

而关联关系是相对较强的关系,一般表现为一个类包含一个类型为另外一个类的属性。

聚合关系、组合关系与关联关系有何区别和联系?

聚合关系、组合关系和关联关系实质上是对象间的关系(继承和实现是类与类和类与接口间的关系)。

从语意上讲,关联关系中两种对象间一般是平等的,而聚合和组合则代表整体和部分间的关系。聚合与组合的区别主要体现在实现上和生命周期的管理上。

聚合关系与组合关系都表示整体与部分的关系,有何区别?

聚合关系中,部分对象的生命周期独立于整体对象的生命周期,或者整体对象消亡后部分对象仍然可以独立存在。

组合关系中,部分类对象的生命周期由整体对象控制,一旦整体对象消亡,部分类的对象随即消亡。

同时在组合关系中,部分类的对象只属于某一个确定的整体类对象;而在聚合关系中,部分类对象可以属于一个或多个整体类对象。


参考文章


UML 类图详解
https://luoyuy.top/posts/4f89fbcb913c/
作者
LuoYu-Ying
发布于
2023年1月18日
许可协议