UML 类图
目录
-
[1 概述](#1 概述)
-
[2 类图MerMaid基本表示法](#2 类图MerMaid基本表示法)
-
[3 类关系详解](#3 类关系详解)
- [3.1 实现和继承](#3.1 实现和继承)
- [3.1.1 实现(Realization)](#3.1.1 实现(Realization))
- [3.1.2 继承/泛化(Inheritance/Generalization)](#3.1.2 继承/泛化(Inheritance/Generalization))
- [3.2 聚合和组合](#3.2 聚合和组合)
- [3.2.1 组合(Composition)](#3.2.1 组合(Composition))
- [3.2.2 聚合(Aggregation)](#3.2.2 聚合(Aggregation))
- [3.3 关联和依赖](#3.3 关联和依赖)
- [3.3.1 关联(Association)](#3.3.1 关联(Association))
- 重数性(Multiplicity)
- [导航性 (navigability)](#导航性 (navigability))
- [3.3.1.1 关联关系的分类](#3.3.1.1 关联关系的分类)
- [3.3.1.1.1 双向关联(无箭头或双箭头)](#3.3.1.1.1 双向关联(无箭头或双箭头))
- [3.3.1.1.2 单向关联(单箭头)](#3.3.1.1.2 单向关联(单箭头))
- [3.3.1.1.3 限制关联](#3.3.1.1.3 限制关联)
- [3.3.1.1.4 自关联和递归关联](#3.3.1.1.4 自关联和递归关联)
- [依赖关系 (Dependency)](#依赖关系 (Dependency))
- [3.3.1 关联(Association)](#3.3.1 关联(Association))
- [3.4 类关系的深度解析](#3.4 类关系的深度解析)
- [3.4.1 基础关系矩阵](#3.4.1 基础关系矩阵)
- [3.4.2 关系判定三维度](#3.4.2 关系判定三维度)
- [3.4.3 特殊关系辨析](#3.4.3 特殊关系辨析)
- [3.4.4 建模实践原则](#3.4.4 建模实践原则)
- [3.4.5 关系速查口诀](#3.4.5 关系速查口诀)
- [3.1 实现和继承](#3.1 实现和继承)
-
[4 注意事项](#4 注意事项)
-
[5 应用建议](#5 应用建议)
-
参考资料
1 概述
在软件工程中,统一建模语言 (UML) 中的类图Class Diagram是一种静态结构图,它通过显示系统的类、它们的属性、操作 (或方法) 以及对象之间的关系来描述系统的结构。
- 定义 :UML(Unified Modeling Language统一建模语言)类图是用来描述系统中类的静态结构和它们之间的关系的一种图。
- 类的静态结构
- 类的属性与方法
- 类之间的关系,例如:依赖、关联、继承等关系。
- 系统组成部分的协作关系
- 作用 :
- 系统分析和设计:帮助理解系统的结构和功能。
- 代码可视化:将代码结构映射到类图上,便于理解和维护。
- 团队沟通:作为统一的建模语言,促进团队成员之间的交流和协作。
- 类图的基本元素
- 类(Class):表示系统中的实体或概念,通常用矩形表示。
- 接口(Interface):用圆角矩形表示,包含接口名和方法。
- 对象(Object):类的实例,用矩形框带有下划线表示。
- 属性(Attribute):类的特性或数据成员,用带有属性名和类型的矩形表示。
- 操作(Operation):类的行为或方法,用带有操作名和参数的椭圆表示。
- 关系(Relationship):表示类之间的联系,如继承、关联、聚合、组合等。
- 我常用的UML工具
- MerMaid
- 如果类图复杂,使用Draw.io将MerMaid代码导入并调整布局和扩展
2 类图MerMaid基本表示法
3 类关系详解
3.1 实现和继承
3.1.1 实现(Realization)
-
定义::表示类与接口之间的关系
-
特点:
- 类实现接口定义的方法
- 支持多接口实现
-
UML表示:空心三角形箭头 + 虚线 (<|...) ,箭头指向接口
-
代码示例
javainterface Flyable { void fly(); } class Bird : Flyable { public void fly() { /*...*/ } }
Flyable Bird
3.1.2 继承/泛化(Inheritance/Generalization)
-
定义:父子类之间的"is-a"关系
-
特点:
- 子类继承父类属性和方法
- 支持多态性
- 父类更通用,子类更具体
-
UML表示:空心三角箭头 + 实线 (<| --) ,从子类指向父类。
-
代码示例:
c#class Animal {} class Dog : Animal {}
Animal Dog
3.2 聚合和组合
3.2.1 组合(Composition)
-
定义:强聚合关系,部分不能脱离整体存在
-
特点:
- 强生命周期绑定
- 整体负责部分的创建与销毁
-
UML表示 :实心菱形(整体端)+ 实线 (
*--
),整体 *-- 部分 -
示例:
- 在线学习平台的课程与课程章节:一个在线学习平台的课程通常由多个章节组成。课程作为一个整体,课程章节作为部分,章节的存在和使用完全依赖于所属的课程。例如,当一个编程语言课程创建后,课程中添加的各个章节(如基础语法章节、面向对象编程章节等)才会被学生学习,只有在课程被访问时,其章节才会被展示和学习,如果该课程从平台上下架删除,那么相应章节也会被删除,无法脱离课程单独存在或被其他课程使用。
-
代码实现:
javausing System; using System.Collections.Generic; // 课程章节类 public class CourseChapter { private string chapterName; private string chapterContent; public CourseChapter(string chapterName, string chapterContent) { this.chapterName = chapterName; this.chapterContent = chapterContent; } public void DisplayChapterInfo() { Console.WriteLine($"章节名称:{chapterName},章节内容:{chapterContent}"); } } // 课程类 public class OnlineCourse { private string courseName; private List<CourseChapter> chapters = new List<CourseChapter>(); public OnlineCourse(string courseName) { this.courseName = courseName; } public void AddChapter(string chapterName, string chapterContent) { CourseChapter chapter = new CourseChapter(chapterName, chapterContent); chapters.Add(chapter); } public void DisplayCourseInfo() { Console.WriteLine($"课程名称:{courseName}"); Console.WriteLine("课程包含的章节:"); foreach (CourseChapter chapter in chapters) { chapter.DisplayChapterInfo(); } } } // 测试类 public class Program { public static void Main() { OnlineCourse javaCourse = new OnlineCourse("C# 编程教程"); javaCourse.AddChapter("基础语法", "介绍了的变量、数据类型、运算符等基础语法知识"); javaCourse.AddChapter("面向对象编程", "讲解了 中的类、对象、继承、多态等面向对象编程概念"); javaCourse.DisplayCourseInfo(); } }
contains CourseChapter - chapterName: string - chapterContent: string +CourseChapter(string, string) +DisplayChapterInfo() OnlineCourse - courseName: string - chapters: List<CourseChapter> +OnlineCourse(string) +AddChapter(string, string) +DisplayCourseInfo()
3.2.2 聚合(Aggregation)
-
定义:"has-a"关系,整体与部分可独立存在
-
特点:
- 弱包含关系
- 生命周期不绑定
-
UML表示:空心菱形(整体端)+ 实线箭头,整体o-- 部分
-
示例:
- 订单(Order)与订单项(OrderItem)是聚合关系,订单 "包含" 订单项,但订单项可以独立于订单存在(例如可以在库存管理中单独处理订单项)。
- 生命周期不绑定:即使订单被删除,订单项对象本身仍然可以存在,它们只是不再被该订单关联而已。
- 订单类通过维护一个订单项列表来管理多个订单项,这体现了整体(订单)与部分(订单项)之间的弱包含关系。
-
代码实现:
javausing System; using System.Collections.Generic; // 订单项类 public class OrderItem { public int ProductId { get; set; } public string ProductName { get; set; } public decimal Price { get; set; } public int Quantity { get; set; } public OrderItem(int productId, string productName, decimal price, int quantity) { ProductId = productId; ProductName = productName; Price = price; Quantity = quantity; } } // 订单类 public class Order { public int OrderId { get; set; } public DateTime OrderDate { get; set; } public List<OrderItem> OrderItems { get; set; } public Order(int orderId, DateTime orderDate) { OrderId = orderId; OrderDate = orderDate; OrderItems = new List<OrderItem>(); } // 添加订单项 public void AddOrderItem(OrderItem item) { OrderItems.Add(item); } } public class Program { public static void Main() { // 创建订单 Order order = new Order(1001, DateTime.Now); // 创建订单项 OrderItem item1 = new OrderItem(201, "Laptop", 800.00m, 1); OrderItem item2 = new OrderItem(202, "Mouse", 20.00m, 2); // 将订单项添加到订单 order.AddOrderItem(item1); order.AddOrderItem(item2); // 输出订单信息 Console.WriteLine($"Order ID: {order.OrderId}"); Console.WriteLine($"Order Date: {order.OrderDate}"); Console.WriteLine("Order Items:"); foreach (var item in order.OrderItems) { Console.WriteLine($"Product ID: {item.ProductId}, Product Name: {item.ProductName}, Price: {item.Price}, Quantity: {item.Quantity}"); } } }
contains Order +int OrderId +DateTime OrderDate +List<OrderItem> OrderItems +Order(int orderId, DateTime orderDate) +AddOrderItem(OrderItem item) : void OrderItem +int ProductId +string ProductName +decimal Price +int Quantity +OrderItem(int productId, string productName, decimal price, int quantity)
3.3 关联和依赖
3.3.1 关联(Association)
-
定义 :类之间的结构型 关系,表示对象间的持久连接,可能有多重性 和导航性
重数性(Multiplicity)
重数表示类之间的实例数量关系,可以用一个整数范围表示,如
0..1
、1..*
等。例如,如果一个客户可以在商店中购买至少0个、最多1个产品,则可以将关联关系的重数设置为
0..1
。导航性 (navigability)
可以通过一个类的实例访问与之关联的另一个类的实例。
导航性可以通过在关联关系线上添加箭头表示。例如,如果
ClassA
可以导航到ClassB
,则可以在关联关系线上添加一个从ClassA
指向ClassB
的箭头。 -
UML表示:实线+箭头(- - >),可标注角色名和多重性
3.3.1.1 关联关系的分类
3.3.1.1.1 双向关联(无箭头或双箭头)
默认是双向的。
示例:在电商公司中,每个客户可以有多个订单,而每个订单都属于一个特定的客户。当客户创建新订单时,系统会自动将客户与订单关联起来。
-
代码
c#public class Customer { public Guid Id { get; } = Guid.NewGuid(); public string Name { get; set; } public List<Order> Orders { get; } = new List<Order>(); // 导航到订单 } public class Order { public string OrderNumber { get; } public DateTime CreateTime { get; } = DateTime.Now; public Customer Owner { get; } // 导航到客户 public Order(Customer owner) { OrderNumber = $"ORD-{DateTime.Now:yyyyMMddHHmmss}"; Owner = owner; owner.Orders.Add(this); // 双向关联建立 } } // 使用示例 var customer = new Customer { Name = "科技公司" }; var order1 = new Order(customer); var order2 = new Order(customer); Console.WriteLine($"{customer.Name}的订单数:{customer.Orders.Count}");
-
MerMiad示例
"导航到订单" 1 N Customer + Guid Id + string Name + List Orders Customer() Order + string OrderNumber + DateTime CreateTime + Customer Owner Order(Customer)
3.3.1.1.2 单向关联(单箭头)
类的关联关系也可以是单向的,单向关联用带箭头的实线表示。
在物流配送系统中,每个包裹可以有多个运输标签,用于指示包裹的目的地。
-
代码
c#public class Package { public string TrackingNumber { get; } = Guid.NewGuid().ToString("N"); public List<TransportLabel> Labels { get; } = new(); // 单向导航到运输标签 } public class TransportLabel { public string Barcode { get; } = Guid.NewGuid().ToString("N").Substring(0, 12); public string Destination { get; set; } } // 使用示例 var package = new Package(); package.Labels.Add(new TransportLabel { Destination = "上海仓库" }); package.Labels.Add(new TransportLabel { Destination = "北京分拨中心" }); Console.WriteLine($"包裹跟踪号:{package.TrackingNumber}");
-
MerMaid
包含多个运输标签 1 N Package +string TrackingNumber +List Labels TransportLabel +string Barcode +string Destination
3.3.1.1.3 限制关联
限定关联具有限定符
限定符的作用类似HashMap中的键(key),用于从一个集合中选择一个或多个对象。
例如,在一个企业资源规划(ERP)系统中,每个用户可以在不同的业务场景下具有不同的角色。
c#
public class User {
private Map<String, Role> roles;
public Role getRole(String scenario){
return roles.get(scenario);
}
}
public class Role {
}

3.3.1.1.4 自关联和递归关联
-
自关联(Self-association):表示一个类与自身相关联。
例如,一个公司可以拥有多个子公司,而子公司也可以有自己的子公司。
c#public class Node { private Node subNode; }
-
递归关联(Recursive association):与自关联类似,但更强调关系的传递性。
例如,一个文件夹可以包含多个子文件夹,子文件夹也可以包含其他子文件夹。
Children Creates TreeNode +Value: int +Children: List +TreeNode(value: int) +AddChild(child: TreeNode) Program +Main() +PrintTree(node: TreeNode, level: int)
依赖关系 (Dependency)
-
概念:依赖则表示一个类在某种程度上依赖于另一个类的定义。
-
特点:
- 是一种使用关系,通常是短暂的,例如一个类的方法内部使用到另一个类。
- 侧重于描述一个类对另一个类的功能或服务的使用,而不涉及持有对方的实例或对象。
- 依赖关系通常是临时性的、相对不稳定的,并且依赖的方向是从依赖者指向被依赖者。
-
UML表示:虚线箭头(... >),可标注角色名和多重
-
示例:
c#using System; namespace DependencyExample { // EmailService 类,用于发送邮件 public class EmailService { public void SendEmail(string to, string subject, string body) { Console.WriteLine($"Sending email to {to}: {subject} - {body}"); } } // Customer 类,依赖 EmailService 类来发送邮件 public class Customer { public string Email { get; set; } // 在方法内部使用 EmailService 类,表现出依赖关系 public void NotifyByEmail(string subject, string message) { EmailService emailService = new EmailService(); emailService.SendEmail(Email, subject, message); } } class Program { static void Main(string[] args) { Customer customer = new Customer(); customer.Email = "[email protected]"; // 调用 Customer 类的方法,触发对 EmailService 类的依赖 customer.NotifyByEmail("Order Confirmation", "Your order has been confirmed."); } } }
- MerMaid类图
Uses Creates and uses EmailService +SendEmail(to: string, subject: string, body: string) Customer -Email: string +NotifyByEmail(subject: string, message: string) Program +Main(args: string[])
- MerMaid类图
3.4 类关系的深度解析
3.4.1 基础关系矩阵
关系类型 | 强度 | 生命周期 | UML表示 | 代码特征 | 典型示例 |
---|---|---|---|---|---|
依赖 | ★ | 临时 | `...>` 虚线 | 方法参数/局部变量 | `Order ...> Payment` |
关联 | ★★ | 独立 | `- ->` 实线 | 成员变量持有引用 | `Teacher --> Student` |
聚合 | ★★★ | 部分独立 | `- -` 空心菱形 | 外部传入部分对象 | `Library o-- Book` |
组合 | ★★★★ | 依赖整体 | `- -` 实心菱形 | 内部创建部分对象 | `School *-- Classroom` |
3.4.2 关系判定三维度
-
生命周期耦合度
强依赖 弱依赖 无依赖 组合 整体销毁则部分消亡 聚合 整体销毁不影响部分 关联 对象独立存在
-
对象控制权
- 强控制:组合(整体创建/销毁部分)
- 弱控制:聚合(外部管理部分对象)
- 无控制:关联(平等协作关系)
-
业务语义表现
C#// 组合关系示例 class Human { Heart heart = new Heart(); // 内部创建不可替换 } // 聚合关系示例 class Car { Tire[] tires; // 外部装配可更换 } // 关联关系示例 class Professor { List<Student> advisees; // 平等协作 }
3.4.3 特殊关系辨析
-
聚合 vs 关联的灰度边界
-
共性特征:均通过成员变量持有引用
-
核心差异:
markdown1. [聚合] 隐含整体-部分语义(汽车-轮胎) 2. [关联] 强调平等协作关系(学生-课程)
-
-
记忆决策树
markdown是否整体-部分关系? ├─ 是 → 部分能否独立存在? │ ├─ 能 → 聚合(空心菱形) │ └─ 否 → 组合(实心菱形) └─ 否 → 是否持久持有? ├─ 是 → 关联(箭头实线) └─ 否 → 依赖(箭头虚线)
3.4.4 建模实践原则
- 语义优先原则
- 避免仅通过代码结构判断关系类型
- 示例:即使同样使用成员变量,
Professor-Student
是关联,Car-Tire
是聚合
- 生命周期驱动设计
- 组合关系应严格满足:
整体.create(部分) && 整体.delete(部分)
- 反例:使用组合表示可更换的汽车发动机将导致设计僵化
- 组合关系应严格满足:
- 模式适配策略
- 聚合关系常对应:对象池模式、共享组件模式
- 组合关系常对应:建造者模式、工厂方法模式
3.4.5 关系速查口诀
text
👐 依赖弱,参数传,工具用完不再看
🤝 关联弱,成员留,合作长久不停休
🚗 聚合中,外部传,轮胎汽车换自由
❤️ 组合强,内部有,人心同命共腐朽
4 注意事项
- 保持简洁和清晰,避免过多的细节,不要过度设计关系
- 正确表达关系和多重性。避免循环依赖,
- 保持合理抽象层次,控制类关系的层级深度
- 及时更新类图以反映系统的变化。
- 保持类图与代码实现的一致性
5 应用建议
- 优先使用组合/聚合代替继承(设计原则)
- 接口实现增强系统扩展性
- 合理控制关联关系的复杂度
- 依赖关系常用于模块解耦