UML 2.5 类图 6 种关系辨析:从代码反推 3 个实战案例
在软件开发中,类图是描述系统静态结构的重要工具。对于初学者来说,理解类图中各种关系的区别往往是一个挑战。本文将采用"代码反推"的视角,通过三个实战案例,深入解析UML类图中六种核心关系的语义差异和代码表现。
1. 类图关系基础:六种核心关系解析
类图中六种核心关系包括泛化、实现、关联、聚合、组合和依赖。这些关系在代码中有不同的表现形式,理解它们的差异对设计良好的软件架构至关重要。
1.1 泛化关系(Generalization)
泛化关系表示类与类之间的继承关系,是最容易理解的一种关系。在Java中,它通过extends关键字实现:
// 父类 class Vehicle { void move() { System.out.println("Vehicle is moving"); } } // 子类继承父类 class Car extends Vehicle { @Override void move() { System.out.println("Car is driving"); } }关键特征:
- 空心三角形箭头指向父类
- 子类继承父类的属性和方法
- 可以实现多态
1.2 实现关系(Realization)
实现关系表示类与接口之间的关系,在Java中通过implements关键字实现:
// 接口定义 interface Drivable { void drive(); } // 类实现接口 class Sedan implements Drivable { @Override public void drive() { System.out.println("Sedan is being driven"); } }关键特征:
- 空心三角形虚线箭头指向接口
- 类必须实现接口中定义的所有方法
- 支持多接口实现
1.3 关联关系(Association)
关联关系表示类之间的长期、稳定的连接,通常表现为成员变量:
class Teacher { private List<Student> students; public void addStudent(Student s) { students.add(s); } } class Student { private Teacher advisor; public void setAdvisor(Teacher t) { this.advisor = t; } }关键特征:
- 普通实线箭头,指向被关联方
- 可以是单向或双向
- 关联强度中等,生命周期独立
2. 整体-部分关系:聚合与组合辨析
聚合和组合都是特殊的关联关系,表示整体与部分的关系,但它们在生命周期管理上有重要区别。
2.1 聚合关系(Aggregation)
聚合表示"has-a"关系,部分可以独立于整体存在:
class Department { private List<Employee> employees; public void addEmployee(Employee e) { employees.add(e); } public void removeEmployee(Employee e) { employees.remove(e); } } class Employee { // 员工可以独立于部门存在 }关键特征:
- 空心菱形箭头指向整体
- 部分可以属于多个整体
- 整体不控制部分的生命周期
2.2 组合关系(Composition)
组合是更强的聚合关系,部分不能独立于整体存在:
class Car { private Engine engine; public Car() { this.engine = new Engine(); // 引擎随车创建 } } class Engine { // 引擎不能独立于车存在 }关键特征:
- 实心菱形箭头指向整体
- 部分只能属于一个整体
- 整体控制部分的生命周期
聚合与组合对比表:
| 特征 | 聚合关系 | 组合关系 |
|---|---|---|
| 生命周期 | 独立 | 依赖整体 |
| 多重性 | 多对多 | 一对一 |
| 代码表现 | 通过setter方法注入 | 在构造函数中创建 |
| UML表示 | 空心菱形 | 实心菱形 |
3. 依赖关系:最弱但最常见
依赖关系表示类之间的临时性关系,通常表现为方法参数、局部变量或返回值:
class OrderProcessor { public void process(Order order) { // 使用Order对象 order.calculateTotal(); // 创建临时Logger对象 Logger logger = new Logger(); logger.log("Order processed"); } }关键特征:
- 虚线箭头指向被依赖方
- 临时性关系,不保持长期引用
- 最常见的类关系类型
4. 实战案例解析:从代码到类图
4.1 电商订单系统案例
代码片段:
// 抽象基类 abstract class User { private String name; public abstract void login(); } // 具体实现 class Customer extends User { private List<Order> orders; @Override public void login() { System.out.println("Customer login"); } public void placeOrder(Order order) { orders.add(order); } } class Order { private List<OrderItem> items; private Payment payment; public void addItem(Product p, int quantity) { items.add(new OrderItem(p, quantity)); } public void setPayment(Payment p) { this.payment = p; } } class OrderItem { private Product product; private int quantity; public OrderItem(Product p, int q) { this.product = p; this.quantity = q; } }类图分析:
- Customer与User:泛化关系(继承)
- Customer与Order:关联关系
- Order与OrderItem:组合关系
- Order与Payment:关联关系
- OrderItem与Product:关联关系
- placeOrder方法参数:依赖关系
4.2 公司组织架构案例
代码片段:
interface IEmployee { void work(); } class Department { private Manager manager; private List<Staff> staffs; public Department(Manager m) { this.manager = m; this.staffs = new ArrayList<>(); } } class Manager implements IEmployee { public void work() { System.out.println("Managing department"); } } class Staff implements IEmployee { private Computer computer; public Staff(Computer c) { this.computer = c; } public void work() { computer.use(); } } class Computer { public void use() { System.out.println("Computer is being used"); } }类图分析:
- Manager/Staff与IEmployee:实现关系
- Department与Manager:组合关系
- Department与Staff:聚合关系
- Staff与Computer:关联关系
4.3 车辆租赁系统案例
代码片段:
class RentalSystem { public RentalContract rent(Vehicle v, Customer c, DateRange d) { return new RentalContract(v, c, d); } } abstract class Vehicle { public abstract void start(); } class Car extends Vehicle { private Engine engine; private List<Wheel> wheels; public Car() { this.engine = new Engine(); this.wheels = new ArrayList<>(); for(int i=0; i<4; i++) { wheels.add(new Wheel()); } } @Override public void start() { engine.ignite(); } } class RentalContract { private Vehicle vehicle; private Customer customer; private DateRange dateRange; public RentalContract(Vehicle v, Customer c, DateRange d) { this.vehicle = v; this.customer = c; this.dateRange = d; } }类图分析:
- Car与Vehicle:泛化关系
- Car与Engine:组合关系
- Car与Wheel:组合关系
- RentalSystem与Vehicle/Customer:依赖关系
- RentalContract与Vehicle/Customer:关联关系
5. 关系辨析决策树
当不确定该使用哪种关系时,可以按照以下决策流程进行判断:
是否是"is-a"关系?
- 是 → 泛化关系(继承)
是否是接口实现?
- 是 → 实现关系
是否是"has-a"关系?
- 是 → 进入整体-部分判断:
- 部分能否独立存在?
- 能 → 聚合关系
- 不能 → 组合关系
- 部分能否独立存在?
- 是 → 进入整体-部分判断:
是否是临时性使用?
- 是 → 依赖关系
以上都不是 → 关联关系
实际应用技巧:
- 优先考虑语义而非语法
- 组合关系通常表现为构造函数中创建部件对象
- 聚合关系通常通过setter方法注入部件
- 依赖关系最常见于方法参数和局部变量
掌握这些关系的区别和应用场景,能够帮助开发者设计出更加清晰、灵活的类结构,为构建可维护的软件系统打下坚实基础。