一、UML简介
我们在进行软件开发的时候,如果只靠脑子想,只有一个看不见的软件规划、软件架构,可能写程序时会影响你的思路的清晰,或者中间间断了以后会忘记当初的规划而要重新回忆或重新规划。如果我们可以把程序设计用图形表达出来,就会让我们的思路很清晰,也很容易进行合理的优化,我们和其他的软件开发人员或者用户就能够进行更好的沟通。
面向对象设计图应运而生,它能清楚的描述以下几个问题:
- 类,包括数据成员和函数成员。
- 对象,类的实例。
- 类及对象的关系,继承或者包含。
- 类及对象之间的联系,相互作用与消息传递等。
现在国际上标准的面向对象标记方法称为UML,即统一建模语言。这种标记分为两类图形符号:
- 表示符号和连接符号。表示符号用来表示类和对象。
- 连接符号用来表示类和对象之间的关系和联系。
二、UML图种类
UML中有9种图:类图、对象图、用例图、顺序图、协作图、状态图、活动图、组件图和实施图。这里只介绍下类图和对象图。
- 类图:

- 对象图:

ML中类及对象的关系有以下几种:依赖、关联、聚合、组合、泛化和实现。
-
依赖关系
如果类A使用了类B,或者说如果类B的变化会影响类A,则说类A依赖于类B。一般有以下几种情况属于依赖关系:类A调用类B的成员函数;类B的对象是类A的成员变量;类A的成员函数使用了类B类型的参数。依赖关系用带箭头的虚线表示。

-
关联关系

重数A表示类B的每个对象与类A的多少个对象相关联,重数B则表示类A的每个对象与类B的多少个对象发生作用。比如,老师和学生的关联,老师类的重数应该是1,学生类的重数可能是n。 -
聚合关系

聚合表示类之间的关系是整体和部分的关系,但是聚合关系中的整体和部分是可以分开的。比如,我们可以选择某个主板、硬盘、机箱等配件组装一台电脑,以后这台电脑可以随时更换配件,还一样能够组成一台电脑。聚合关系用带空心菱形的实线表示。 -
组合关系

组合关系也是整体和部分的关系,但是它与聚合关系不同的是,整体和部分是不可以分开的。比如,我们的房子由客厅、卧室、厨房等组合而成,房子不能和别人的房子对换客厅等任何房间,客厅、卧室等不能和房子分开。组合关系用带实心菱形的实线表示。 -
泛化关系
如果类A和类B是从类C继承的子类,那么类C就是类A和类B的泛化。泛化关系用带空心三角形的实线表示:

-
实现关系

实现关系用来说明接口和实现接口的类之间的关系。实现关系图用带空心三角形的虚线表示。
三、UML类图
3.1 绘制 UML 类图
-
第 1 步:识别类 (Identify the Classes)
首先,你需要从需求或问题描述中找出核心的名词,这些通常是你的类。- 示例:在“一个用户可以创建多个订单”这个需求中,
用户和订单就是两个核心类。
- 示例:在“一个用户可以创建多个订单”这个需求中,
-
第 2 步:定义类的三大要素
每个类在类图中都由一个分为三栏的矩形表示:- 类名 (Class Name):位于顶部,居中。首字母大写。
- 示例:
User
- 示例:
- 属性 (Attributes):位于中间,列出类的成员变量。
- 格式:
[可见性] 属性名 : 类型 [ = 默认值] - 示例:
- id : int,+ name : string
- 格式:
- 方法 (Operations/Functions):位于底部,列出类的成员函数。
- 格式:
[可见性] 方法名(参数列表) : 返回类型 - 示例:
+ getName() : string,- calculateTotal() : double
- 格式:
- 类名 (Class Name):位于顶部,居中。首字母大写。
可见性符号:
+Public (公共)-Private (私有)#Protected (受保护)
一个完整的类图元素示例:
+-------------------+
| User |
+-------------------+
| - id : int |
| - name : string |
| - email : string |
+-------------------+
| + User(name, email) |
| + getName() : string |
| + setEmail(newEmail : string) : void |
+-------------------+
-
第 3 步:建立类之间的关系 (Establish Relationships)
这是类图的核心。类与类之间不是孤立的,它们通过各种关系协同工作。-
关联 (Association)
- 含义:表示两个类之间存在某种静态的、长期的联系。
- 符号:一条实线。
- 示例:
User和Order之间是关联关系。 - 可以添加多重性 (Multiplicity):表示一个类的实例可以与另一个类的多少个实例相关联。
1:一个*:零个或多个0..1:零个或一个2..5:两个到五个
- 关联示例:一个
User可以有多个Order,而一个Order只属于一个User。
User "1" -- "*" Order(这读作:一个 User 关联零个或多个 Order)
-
聚合 (Aggregation)
- 含义:表示“整体-部分”关系,其中部分可以脱离整体而独立存在。是一种较弱的拥有关系。
- 符号:一条带空心菱形的实线,菱形指向“整体”。
- 示例:
Car和Wheel。车包含轮子,但轮子即使从车上卸下也仍然是一个轮子。
Car <>- Wheel -
组合 (Composition)
- 含义:也表示“整体-部分”关系,但部分不能脱离整体而存在。是一种强的拥有关系,整体负责部分的生命周期。
- 符号:一条带实心菱形的实线,菱形指向“整体”。
- 示例:
Person和Heart。心脏是人的一部分,如果人不存在了,心脏也就失去了其作为“人心脏”的意义。
Person <*>-- Heart -
继承 (Inheritance / Generalization)
- 含义:表示“is-a”关系,子类继承父类的属性和方法。
- 符号:一条带空心三角形的实线,三角形指向父类。
- 示例:
Student和Teacher都是Person。
Student --|> Person Teacher --|> Person -
实现 (Realization)
- 含义:表示一个类实现了一个接口(或多个接口)。
- 符号:一条带空心三角形的虚线,三角形指向接口。
- 示例:
Car类实现了Driveable接口。
Car --|.. Driveable -
依赖 (Dependency)
- 含义:表示一个类在某个特定时刻临时使用到了另一个类。这是一种最弱的关系。
- 符号:一条带箭头的虚线,箭头指向被依赖的类。
- 示例:
OrderProcessor在处理订单时,需要调用EmailService来发送邮件。OrderProcessor依赖于EmailService。
OrderProcessor ..> EmailService
-
-
使用工具绘制
手动绘制容易出错且难以维护。建议使用专业的 UML 工具:- 入门级:Draw.io (免费在线)、Lucidchart
- 专业级:StarUML、Visual Paradigm
- IDE 集成:IntelliJ IDEA Ultimate、Visual Studio (需安装插件)
3.2 绘制类图需要注意的关键点
1. 明确建模目的,避免过度设计
- 问自己:“这张图是给谁看的?想表达什么核心概念?”
- 不要试图将代码的每一个细节都画出来。类图是设计蓝图,不是代码的逐行翻译。
- 保持简洁:只画出关键的类、属性、方法和关系。过多的细节会让图变得混乱,失去沟通价值。
2. 正确使用关系,避免滥用
- 关联 vs 依赖:如果两个类总是一起出现,有长期联系,用关联。如果只是某个方法临时调用一下,用依赖。
- 聚合 vs 组合:这是最容易混淆的。问自己一个问题:“如果‘整体’被销毁,‘部分’还能独立存在吗?”
- 能 → 聚合 (Aggregation)
- 不能 → 组合 (Composition)
- 优先使用关联,而非继承:不要为了复用少量代码而建立继承关系。过度使用继承会导致类层次结构僵化。优先考虑组合或依赖。
3. 命名规范要统一
- 类名:使用名词,首字母大写,采用驼峰命名法 (CamelCase),如
OrderItem。 - 属性和方法名:使用有意义的名称,首字母小写,采用驼峰命名法,如
orderDate,calculateTotal()。 - 一致性:在整个项目中保持相同的命名风格。
4. 善用多重性 (Multiplicity)
- 多重性是类图中传递设计信息的关键。不要忘记添加它。
- 例如,
User和Address的关系可能是1对0..*(一个用户可以有零个或多个地址)。
5. 控制图的复杂度
- 拆分大图:如果系统非常复杂,可以使用包图 (Package Diagram) 将类组织成不同的模块(如
UserModule,OrderModule),然后为每个包单独绘制详细的类图。 - 使用接口:接口可以简化类图。通过定义接口,可以隐藏具体实现类的复杂性,只展示系统的交互契约。
6. 保持图与代码的同步
- UML 图是“活的文档”。代码发生重大变更后,必须及时更新相应的类图。
- 否则,这些图会很快过时,失去其作为设计文档和沟通工具的价值。最好使用支持“代码反向生成”的工具。
绘制 UML 类图是一个从抽象到具体的过程。关键在于识别核心概念(类)、定义它们的特征(属性和方法)、并建立它们之间的正确关系。同时,要时刻记住为沟通服务,保持图的清晰、简洁和准确。
3.3 接口
在 UML 类图中,接口(Interface) 是一种抽象的“契约”或“行为规范”,它仅定义了类或组件应该提供的方法(行为)列表,但不包含方法的具体实现,也不存储数据(即无属性)。其核心作用是解耦“行为定义”与“行为实现”,让不同的类可以遵循统一的规范提供功能,同时不依赖彼此的具体实现。
- 纯抽象性:仅包含方法签名(方法名、参数、返回值),无任何实现代码;
- 无状态:不定义属性(不能存储数据),仅描述“能做什么”,不关心“数据如何存储”;
- 多实现性:一个类可以同时实现多个接口(弥补了很多语言中“单继承”的限制);
- 契约性:只要类实现了接口,就必须重写接口中的所有方法,保证行为符合规范。
1. UML类图中接口的两种表示方式
在类图中,接口有两种标准画法,根据场景选择使用:
-
「棒棒糖」表示法(Lollipop Notation)
- 形状:用一个小圆圈表示接口,圆圈下方标注接口名;
- 关系:实现接口的类用带空心三角的虚线连接到接口(三角指向接口),虚线可理解为“遵循契约”。
-
「类式」表示法(Class-like Notation)
- 形状:用一个矩形表示接口,矩形顶部标注
<<interface>>( stereotypes,构造型),下方依次列出接口的方法(无属性); - 关系:与“棒棒糖”表示法一致,实现类用带空心三角的虚线连接到接口。
- 形状:用一个矩形表示接口,矩形顶部标注
2. 接口的典型示例
以“电子设备的远程控制”场景为例:所有可被远程控制的设备(如电视、空调、投影仪),都需要提供“开机”“关机”“调节音量”的功能——这组功能可以抽象为 RemoteControllable 接口,不同设备则实现该接口并提供具体逻辑。
- 用「棒棒糖」表示法绘制的类图
+----------------+ <<interface>>
| TV | RemoteControllable
+----------------+ O
| - channel: int | |
+----------------+ | (虚线,带空心三角)
| + turnOn(): void| |
| + turnOff(): void|----------+
| + adjustVolume(level: int): void |
| + changeChannel(ch: int): void |
+----------------+
+----------------+
| AirConditioner |
+----------------+
| - temperature: int |
+----------------+
| + turnOn(): void|----------+
| + turnOff(): void|---------+
| + adjustVolume(level: int): void |
| + setTemperature(temp: int): void |
+----------------+
- 用「类式」表示法绘制的类图
+----------------+ +--------------------------------+
| TV | | <<interface>> RemoteControllable |
+----------------+ +--------------------------------+
| - channel: int | | + turnOn(): void |
+----------------+ | + turnOff(): void |
| + turnOn(): void| | + adjustVolume(level: int): void|
| + turnOff(): void|-------(虚线,带空心三角)-------------+
| + adjustVolume(level: int): void |
| + changeChannel(ch: int): void |
+----------------+
+----------------+
| AirConditioner |
+----------------+
| - temperature: int |
+----------------+
| + turnOn(): void|-------(虚线,带空心三角)-------------+
| + turnOff(): void|------------------------------------+
| + adjustVolume(level: int): void |
| + setTemperature(temp: int): void |
+----------------+
3. 对应的代码实现
- (以Java为例)
接口定义(契约):
// 接口:定义远程可控设备的统一行为
public interface RemoteControllable {
void turnOn(); // 开机(无实现)
void turnOff(); // 关机(无实现)
void adjustVolume(int level); // 调节音量(无实现)
}
TV类实现接口(遵循契约并提供具体逻辑):
public class TV implements RemoteControllable {
private int channel; // TV的私有属性(接口中无此定义)
// 必须实现接口的所有方法
@Override
public void turnOn() {
System.out.println("电视开机,当前频道:" + channel);
}
@Override
public void turnOff() {
System.out.println("电视关机");
}
@Override
public void adjustVolume(int level) {


2573

被折叠的 条评论
为什么被折叠?



