UML 类图入门:从单个类到类间关系
什么是 UML 类图
UML(Unified Modeling Language,统一建模语言)类图用于描述系统中包含的类以及它们之间的相互关系,帮助简化对系统的理解。它是系统分析和设计阶段的重要产物,也是编码和测试的重要模型依据。
类图主要包含两部分内容:
- 如何通过 UML 描述单个类的结构
- 如何清晰表述类与类之间的关系
单个类的画法
三层结构
UML 中一个类用矩形表示,分为三层:
- 上层:类名
- 中层:属性(成员变量)
- 下层:方法(成员函数)
属性表示法
格式:【可见性】【属性名称】:【类型】 = {缺省值}
- 可见性符号:
+表示 public,#表示 protected,-表示 private - 静态成员:属性名下方加下划线
方法表示法
格式:【可见性】【方法名称】(【参数名:参数类型】):【返回值类型】
- 多个参数用逗号分隔
- 无返回值使用
void - 静态方法同样加下划线
示例:猎人类
以一个猎人(Hunter)类为例,UML 类图如下:
---------------------------------- | Hunter | ---------------------------------- | - m_age : int | | - n_times : static int | ---------------------------------- | + Hunter(age : int) | | + display() : void | | + hunt() : void | | + getHunterCount() : static int| ----------------------------------对应的 C++ 实现:
#include <iostream> using namespace std; class Hunter { private: int m_age; // 非静态成员变量,属于对象 static int n_times; // 静态成员变量,属于类 public: Hunter(int age) : m_age(age) { n_times++; } static int getHunterCount() { // 静态成员函数,只能访问静态成员 return n_times; } void display() const { cout << "猎人年龄:" << m_age << endl; cout << "猎人总数:" << n_times << endl; } void hunt() { cout << "猎人正在狩猎..." << endl; } }; int Hunter::n_times = 0; // 静态成员变量类外初始化 int main() { Hunter h1(25); Hunter h2(30); h1.display(); cout << "猎人总数:" << Hunter::getHunterCount() << endl; h1.hunt(); return 0; }关键点对照:m_age是每个猎人独有的(非静态),n_times是所有猎人共享的计数(静态,UML 中下划线标注)。
抽象类的表示
抽象类特征
- 包含纯虚函数(函数声明后加
= 0) - UML 中类名使用斜体,或标注
<<abstract>> - 不能实例化对象
示例:Animal 抽象类
---------------------------------- | <<abstract>> | | Animal | ---------------------------------- | # name : string | ---------------------------------- | + Animal(name : string) | | + makeSound() : void = 0 | | + display() : void | ---------------------------------- △ | | ---------------------------------- | Dog | ---------------------------------- | - breed : string | ---------------------------------- | + Dog(name, breed : string) | | + makeSound() : void | | + getBreed() : string | ----------------------------------Animal是抽象类(makeSound为纯虚函数),Dog继承并实现了纯虚函数,因此可以实例化。
对应的 C++ 实现:
#include <iostream> #include <string> using namespace std; class Animal { // 抽象类 protected: string name; public: Animal(string n) : name(n) {} virtual void makeSound() const = 0; // 纯虚函数 virtual void display() const { cout << "动物名称:" << name << endl; } virtual ~Animal() {} }; class Dog : public Animal { // 具体类 private: string breed; public: Dog(string n, string b) : Animal(n), breed(b) {} void makeSound() const override { cout << name << "(品种:" << breed << ")在叫:汪汪汪!" << endl; } void display() const override { cout << "狗 - 名称:" << name << ",品种:" << breed << endl; } }; int main() { // Animal a("动物"); // 错误:抽象类不能实例化 Dog dog("小白", "金毛"); Animal* animal = &dog; // 父类指针指向子类对象 animal->display(); animal->makeSound(); return 0; }类之间的六种关系
单个类的画法只是基础,类图真正的价值在于描述类与类之间的关系。常见的有六种:
关联(Association)
类之间的结构化引用关系,一个类的对象持有另一个类的对象作为成员。
- 表示法:实线箭头
A --> B - 示例:猎人持有猎枪,
Hunter类中包含Gun类型的成员变量
Hunter ----------> Gun依赖(Dependency)
一个类在某个地方使用了另一个类(如函数参数、局部变量),是最弱的关系。
- 表示法:虚线箭头
A ..> B - 示例:猎人的
hunt()方法接收Forest类型的参数
Hunter ..> Forest泛化(Generalization)
即继承关系,子类继承父类的属性和行为。
- 表示法:空心三角实线箭头,指向父类
子类 --|> 父类 - 示例:
Dog继承Animal
Dog --|> Animal实现(Realization)
类实现接口(抽象类)中定义的纯虚函数。
- 表示法:空心三角虚线箭头
子类 ..|> 接口 - 示例:
Dog实现Animal接口的makeSound()
Dog ..|> Animal在 C++ 中,泛化和实现通常都通过继承体现:继承有具体实现的父类是泛化,继承只有纯虚函数的抽象类是实现。
聚合(Aggregation)
整体与部分的关系,但部分可以脱离整体独立存在。
- 表示法:空心菱形 + 实线,菱形在整体端
整体 o-- 部分 - 示例:战队包含队员,队员离开战队后仍然存在
Team o-- Member组合(Composition)
整体与部分的关系,但部分不能脱离整体独立存在,生命周期由整体管理。
- 表示法:实心菱形 + 实线,菱形在整体端
整体 *-- 部分 - 示例:汽车包含发动机,发动机离开汽车没有独立意义
Car *-- Engine六种关系速查表
| 关系 | 表示法 | 强度 | 典型场景 |
|---|---|---|---|
| 关联 | A --> B | 中 | 类成员引用另一个类 |
| 依赖 | A ..> B | 弱 | 函数参数、局部变量 |
| 泛化 | `子类 – | > 父类` | 强 |
| 实现 | `类 … | > 接口` | 强 |
| 聚合 | 整体 o-- 部分 | 中 | 整体-部分,部分可独立 |
| 组合 | 整体 *-- 部分 | 强 | 整体-部分,部分不可独立 |
完整示例
用一个简单的系统把上面的关系串起来:
Team o-- Hunter (聚合:战队包含猎人,猎人可独立存在) Hunter --> Gun (关联:猎人持有猎枪) Hunter ..> Forest (依赖:狩猎方法接收森林参数) Hunter --|> Person (泛化:猎人继承自人) Dog ..|> Animal (实现:狗实现动物接口) Car *-- Engine (组合:汽车包含发动机,不可独立)这张图涵盖了六种关系,阅读时从箭头和菱形的形状就能判断关系类型。
小结
UML 类图的学习分两步:
- 掌握单个类的画法:三层结构、属性/方法表示法、可见性符号、静态成员和抽象类的特殊标注
- 理解类间六种关系:关联、依赖、泛化、实现、聚合、组合,每种关系对应不同的箭头或菱形符号
画类图时,先识别系统中有哪些类,再确定类之间的关系,最后用标准符号表达。熟练之后,类图能帮你在写代码之前就把系统结构想清楚,减少后期重构的代价。