UML 中类图与类的关系
类图(Class Diagram)描述系统中类的静态结构。不仅定义系统中的类,表示类之间的联系如关联、依赖、聚合等,也包括类的内部结构(类的属性和操作)。类图是以类为中心来组织的,类图中的其他元素或属于某个类或与类相关联。
类图以反映类的结构(属性、操作)以及类之间的关系为主要目的,描述了软件系统的结构,是一种静态建模方法。类图中的「类」与面向对象语言中的「类」的概念是对应的,是对现实世界中的事物的抽象。

- 类图 Class Diagram:类图描述系统中类的静态结构。不仅定义系统中的类,表示类之间的联系如关联、依赖、聚合等,也包括类的内部结构(类的属性和操作)。
- 对象图(Object Diagram):对象图是类图的实例,几乎使用与类图完全相同的标识。他们的不同点在于对象图显示类的多个对象实例,而不是实际的类。

类图中的事物及解释
类

从上到下分为三部分,分别是类名、属性和操作。
- 类名是必须有的。
- 类如果有属性,则每一个属性都必须有一个名字,另外还可以有其它的描述信息,如可见性、数据类型、缺省值等。
- 类如果有操作,则每一个操作也都有一个名字,其它可选的信息包括可见性、参数的名字、参数类型、参数缺省值和操作的返回值的类型等。参数表和返回类型可选。
类型:
- 接口:一组操作的集合,只有操作的声明而没有实现
- 抽象类:不能被实例化的类,一般至少包含一个抽象操作
- 模板类:一种参数化的类,在编译时把模板参数绑定到不同的数据类型,从而产生不同的类

在 UML 类图中表示具体类
具体类在类图中用矩形框表示,矩形框分为三层:
- 第一层是类名字;
- 第二层是类的成员变量;
- 第三层是类的方法。
成员变量以及方法前的访问修饰符用符号来表示:
| UML 表示 | 说明 | Java 修饰符 | Rose 工具中的表示 |
|---|---|---|---|
+ |
公有 | public |
![]() ![]() |
- |
私有 | private |
![]() |
# |
受保护 | protected |
![]() |
~ |
包内公有 | default:同一个包中的类才能访问的方法和字段 |
|
| 不带符号 | default 或者不关心 |
||
下划线/$ |
static 静态方法、静态字段 |

在 UML 类图中表示抽象类
抽象类在 UML 类图中同样用矩形框表示,但是抽象类的类名以及抽象方法的名字都用斜体字表示。
例 1:

例 2:抽象类的 C++ 与 Java 实现

1 | class Vehicle |
1 | public abstract class Vehicle |
在 UML 类图中表示接口
接口在类图中也是用矩形框表示,但是与类的表示法不同的是,接口在类图中的第一层顶端用构造型 <<interface>> 表示,下面是接口的名字,第二层是方法,如图 3 所示。此外,接口还有另一种表示法,俗称棒棒糖表示法,就是类上面的一根棒棒糖(圆圈 + 实线)。圆圈旁为接口名称,接口方法在实现类中出现。

表示接口时,可以默认 abstract 和 public 。不需要斜体表示。
在 UML 类图中表示包
类和接口一般都出现在包中。

关系

注意,以上的类的关系分类方式仅供参考。在其他资料中可能会有以下说法,本文在此不再深究:
- 依赖关系、关联关系是平级的。依赖关系是一种独立的关系。
- 实现关系是泛化关系的子类型。
关联关系

关联关系描述了类的结构之间的关系。具有方向、名字、角色和多重性等信息。一般的关联关系语义较弱。也有两种语义较强,分别是聚合与组合。
关联关系(Association)是指对象和对象之间的连接,它使一个对象知道另一个对象的属性和方法。在 Java 中,关联关系的代码表现形式为一个对象含有另一个对象的引用。
关联关系有单向关联和双向关联。
- 双向关联:如果两个对象都知道(即可以调用)对方的公共属性和操作,那么二者就是双向关联。
- 单向关联:如果只有一个对象知道(即可以调用)另一个对象的公共属性和操作,那么就是单向关联。大多数关联都是单向关联,单向关联关系更容易建立和维护,有助于寻找可重用的类。
特性:
- 导航性(Navigatity):在 UML 图中,双向关联关系用带双箭头的实线或者无箭头的实线双线表示。单向关联用一个带箭头的实线表示,箭头指向被关联的对象。被指的人不知道有人关联它。
- 多重性(Multipicity):一个对象可以持有其它对象的数组或者集合。在 UML 中,通过放置多重性(multipicity)表达式在关联线的末端来表示:
- 数字:精确的数量
0..n:表示 0 到多个0..1:表示 0 或者 1 个,在 Java 中经常用一个空引用来实现1..*:表示 1 到多个3, 6..9*:即0..n

可以在类名前面加上黑三角表示类之间的关联关系。

聚合关系

聚合(Aggregation)是关联关系的一种特例,是特殊的关联关系。它体现的是整体与部分的拥有关系,即 “has a” 的关系。此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享,所以聚合关系也常称为共享关系。
在 UML 图中,聚合关系用空心菱形加实线箭头表示,空心菱形在整体一方,箭头指向部分一方。
哪边是容器哪边就是菱形箭头,像个小篮子一样。
公司部门与员工的关系,一个员工可以属于多个部门,一个部门撤消了,员工可以转到其它部门。

组合关系

组合(Composition)也是关联关系的一种特例,是语义更强的聚合。它同样体现整体与部分间的包含关系,即 “contains a” 的关系。但此时整体与部分是不可分的,部分也不能给其它整体共享,作为整体的对象负责部分的对象的生命周期(部分和整体具有相同的生命周期)。这种关系比聚合更强,也称为强聚合。如果 A 组合 B,则 A 需要知道 B 的生存周期,即可能 A 负责生成或者释放 B,或者 A 通过某种途径知道 B 的生成和释放。
在 UML 图中,组合关系用实心菱形加实线箭头表示,实心菱形在整体一方,箭头指向部分一方。
组合和聚合代码没什么区别,两个对象之间的关系到底是聚合还是组合看你怎么认为。如果一定要区分,那么如果在删除整体对象的时候,必须删掉部分对象,那么就是组合关系,否则可能就是聚合关系。从业务角度上来看,如果作为整体的对象必须要部分对象的参与,才能完成自己的职责,那么二者之间就是组合关系,否则就是聚合关系。
人包含头、躯干、四肢,它们的生命周期一致。当人出生时,头、躯干、四肢同时诞生。当人死亡时,作为人体组成部分的头、躯干、四肢同时死亡。
汽车与轮胎,汽车作为整体,轮胎作为部分。如果用在二手车销售业务环境下,二者之间就是聚合关系。因为轮胎作为汽车的一个组成部分,它和汽车可以分别生产以后装配起来使用,但汽车可以换新轮胎,轮胎也可以卸下来给其它汽车使用。如果用在驾驶系统业务环境上,汽车如果没有轮胎,就无法完成行驶任务,二者之间就是一个组合关系。
网上书店业务中的订单和订单项之间的关系也是组合关系:如果订单没有订单项,也就无法完成订单的业务。而购物车和商品之间的关系是聚合关系:因为商品的生命周期并不被购物车控制,商品可以被多个购物车共享。
组合关系,代码表现为 Dialog 的属性有 Button 和 TextBox 的对象。

组合关系,员工与时间卡

泛化关系

泛化关系(Generalization)是指对象与对象之间的继承关系。如果对象 A 和对象 B 之间的“is a”关系成立,那么二者之间就存在继承关系,对象 B 是父对象,对象 A 是子对象。比如:一个类(子类、子接口)继承另外一个类(称为父类、父接口)的功能。
面向对象:从底部、从具体开始抽象。思路,现从有的各种各样的子类,再到父类。
在 UML 类图中,泛化关系用空心三角和实线组成的箭头表示,从子类指向父类。关于箭头的方向理解:父类不知道子类的定义,子类一定知道父类的定义。只有在知道对方信息的时候才能指向对方,因此箭头方向是从子类指向父类。
在 Java 中就是 extends 关键字。


实现关系
实现关系是指接口及其实现类之间的关系。在 UML 类图中,实现关系用空心三角和虚线组成的箭头来表示,从实现类指向接口。
在 C++ 语言里面,使用抽象类代替接口,使用泛化关系代替实现关系。在 Java 语言里面,有相应的关键字 interface、implements。
例 1:

例 2:类 Circle、Rectangle 实现了接口 Shape 操作。

1 | class Shape{ |
1 | public interface Shape{ |
依赖关系
依赖(Dependency)关系是一种弱关联关系,描述了一个类的变化对依赖于它的类产生影响的情况。有多种表现形式,例如绑定(bind)、友元(friend)等。
如果对象 A 用到对象 B,但是和 B 的关系不是太明显的时候,就可以把这种关系看作是依赖关系。如果对象 A 依赖于对象 B,则 A “use a” B。比如驾驶员和汽车的关系,驾驶员使用汽车,二者之间就是依赖关系。
在 UML 类图中,依赖关系用一个带虚线的箭头表示,由使用方指向被使用方,表示使用方对象持有被使用方对象的引用。

A 依赖于 B 的表现形式:
- B 为 A 的局部变量或构造器
- B 作为 A 的方法或构造器的参数
- B 作为 A 的方法的返回值
- A 调用 B 的静态方法
代码清单 1 B.java:
1 | public class B { |
代码清单 2 A.java:
1 | public class A { |
例 1:绑定依赖
模板类 Stack<T> 定义了栈相关的操作;IntStack 将参数 T 与实际类型 int 绑定,使得所有操作都针对 int 类型的数据。

1 | template<typename T> |
1 | // 编译器生成 |
例 2:Memento 类和 Originator 类建立了友元依赖关系,以便 Originator 使用 Memento 的私有变量 state。

类图案例
本节将配合研究生课程《系统建模与分析》类图相关案例演示。
图形编辑器
图形编辑器一般都具有一些基本图形,如直线、矩形等,用户可以直接使用基本图形画图,也可以把基本图形组合在一起创建复杂图形。如果区别对待基本图形和组合图形,会使代码变得复杂,而且多数情况下用户认为二者是一样的。组合模式可以用相同的方式处理两种图形。

Graphics:基本图形和组合图形的父类,声明了所有图形共同的操作,如Draw;也声明了专用于组合图形管理子图形的操作,如Add、RemoveLine、Rectangle:基本图形类GroupGraphics:组合图形类,与父类有组合关系,从而可以组合所有图形对象(基本图形和组合图形)
演出售票系统
在用例驱动的开发过程中,通过分析各个用例及参与者得到类图。分析用例图的过程中需要根据面向对象的原则设计类和关系,根据用例的细节设计类的属性和操作。

在这里只考虑以下三个用例:
- Buy tickets:买个人票
- Buy Subscription:买套票
- Make charges:信用卡付款

领域模型与概念类图
领域模型是对领域内概念类或现实世界中对象的可视化表示,也称为概念模型、领域对象模型和分析对象模型。它是更为完整的业务模型的一个特例,不是软件对象的表示。从 UML 的表示法角度,领域模型被描述为一组没有定义操作的类图(概念类、关联、属性)。
为模型建立适当的属性与关联。领域模型表现的是概念类之间的数量关系,对于数量关系的理解可以理解为与 ER 图中相似。
classDiagram
class SalesLineItem {
quantity
}
class Item {
}
class Sale {
date
time
}
class Store {
address
name
}
class Payment {
amount
}
class Register {
}
SalesLineItem "0..1" -- "1" Item : Records-sale-of
SalesLineItem "1..*" -- "1" Sale : Contained-in
Sale "1" -- "1" Payment : Paid-by
Sale "1" -- "1" Register : Captured-on
Item "*" -- "1" Store : Stocked-in
Store "1" -- "1..*" Register : Houses
领域模型两部走:找到概念类(最重要)+ 建立关联(多是数量关联)
领域模型和数据模型是一回事吗?
- 领域模型不是数据模型(持续化数据)
- 在领域模型中不会排除没有明确要求记录其相关信息的类,也不会排除没有属性的概念类
- 在领域内充当纯行为角色,而不是信息角色的概念类也是有效的
找概念类
领域模型中核心部分自然就是概念类的确定。概念类是思想、事物或对象。更正正式地讲,概念类可以从其符号、内涵和外延来考虑。因为领域模型属于分析阶段的产物,还没有进一步的实现,所以很多内容都属于猜想阶段,但是如何尽可能准确地找到系统需要的类,进而找到概念类呢,有以下几个标准:业务对象、真实世界中的对象、事件。领域模型构建时,主要的时间要花在确定概念类上,而不是找关联上。
面向对象开发者在创建类时收到真实世界领域的启发,因此,涉众所设想的领域与其在在在软件的表示之间的表示差异被降低。
使用领域术语:
- 使用地域中现有的名称。在图书馆模型中,将顾客命名为 " 借书者「、」赞助者 " 等,这是图书管理员使用的术语
- 排除无关或超出范围的特性
- 不要凭空增加事物
常见错误: 把应该是概念类的事务表示属性。判别准则: 如果我们认为某概念 X 不是现实中的数字或者文本,那么 X 可能是概念类而不是属性。

描述包含描述事物的信息:如 productDescription 记录 Item 的价格、图片和文字描述。命名方式:项目 - 描述符
- 描述有关商品或服务的描述,独立于任何商务或服务现有实例
- 删除所描述事物的实例后,导致信息丢失,而这些信息是需要维护的,但是被错误低于所删除的事务关联起来
- 减少冗余或重复信息
classDiagram
direction LR
class Item_Bad {
description
price
serial number
itemID
}
class ProductDescription {
description
price
itemID
}
class Item_Good {
serial number
}
ProductDescription "1" -- "*" Item_Good : Describes

建立关联
在找到概念类之后,需要确定的剩余内容就是关联关系了:
- 关联是可以有方向的,可以是单向,也可以是双向。
- 数量关系是根据需求定义的。它定义了类 A 有多少实例可以和类 B 的多少实例关联。其指标是在特定的时刻,而不是在某个时间跨度内的有效关联的实例数量。
在领域建模中,关联不是关于数据流、数据库外键的联系、实例变量或软件方案的对象连接语句;关联声明是针对现实领域从纯概念角度看有意义的关系。这些关系的大部分作为导航(设计模型和数据模型中的)和可见性路径在软件中加以实现。

以「类名-动词短语-类名」的格式为关联命名,其中的动词短语构成了可读的和有意义的顺序。
Sale paid-by CashPayment. 反面示例(动词短语没有增加意义):Sale Uses CashPayment.
Player is-on Square. 反面示例(动词短语没有增加意义):Player Has Square.
关联名首字母应该大写,因为关联表示的是实例之间链接的类元。阅读方向箭头可省略(缺省时,从左到右,从上到下),只表示阅读关联的标记方向,没有其他含义。
本文参考
- 30 分钟学会 UML 类图 - 知乎 (zhihu.com)
- 本科学习笔记《面向对象设计与设计模式》
- 研究生课程《系统建模与分析》相关课件
- 统一建模语言UML(四):领域模型和类图_领域模型图-CSDN博客
- UML 哲学之道——领域模型[四] - 敖毛毛 - 博客园
本文 PlantUML 归档
1 | class Dialog{ |
1 | abstract class Vehicle{ |
1 | abstract class Account{ |
1 | class Stack<T>{ |
1 | abstract class Graphics { |
1 | @startuml |
1 | skinparam groupInheritance 2 |
1 | left to right direction |
1 | class Memento{ |





