Read this post in: de_DEen_USes_ESfr_FRhi_INid_IDjapl_PLpt_PTru_RUvizh_TW

UML类图的全面指南(代码即图示)

💡 注意: 所有图表均以PlantUML格式提供。您可以使用Visual Paradigm 图表即代码.


🔹 UML简介

什么是UML?

“统一建模语言(UML)是一种通用的可视化建模语言,用于指定、可视化、构建和记录软件系统的制品。”—— Rumbaugh 等人,1999年

主要特征:

  • 🎨 可视化符号: 用于建模系统的图形语法

  • 📐 标准化: 自1997年以来由OMG采纳的标准

  • 🔧 语言,而非方法: 定义符号,而非流程

  • 🌐 应用范围广泛: 可建模业务流程、系统功能、代码结构和数据库模式

UML并非什么

误解 事实
一种开发方法论 仅仅是一种建模符号
一种编程语言 抽象规范语言
仅用于面向对象编程 适用于数据库、业务建模等
在各个方面都精确定义 早期版本中仍存在一些语义模糊性

🔹 历史与标准化

演进时间线

The Evolution of Unified Modeling Language (UML)

1965-1970:Simula-67(第一种面向对象语言)
     ↓
1970年代-1980年代:施乐帕克研究中心的Smalltalk
     ↓
1984:本斯特拉斯特鲁普推出C++
     ↓
1988-1992:面向对象方法的泛滥(Booch、OMT、OOSE等)
     ↓
1994:伦巴ugh加入博奇在Rational公司 → 统一工作开始
     ↓
1995:UML 0.8草案发布
     ↓
1996:OMG发布标准建模语言的征求建议书
     ↓
1997:OMG采纳UML 1.1(11月14日)
     ↓
2000:UML 1.3正式发布
     ↓
2003:UML 1.5发布;UML 2.0超结构被接受

为什么UML赢得了“方法之战”

  • 将50多种竞争性的面向对象方法整合为一种标准

  • 得到主要行业巨头(IBM、微软、甲骨文、惠普)的支持

  • 提供了可扩展机制以支持定制

  • 成为面向对象建模的事实标准

⚠️ 批判性视角:有人认为UML是“由委员会设计的怪物语言”,早期版本语义不精确。


🔹 类与属性

类结构

UML中的类以最多包含三个部分的矩形表示。

@startuml
class Student {
  firstName: String
  lastName: String
  email[0..1]: String
  encryptedPW: String
  + totalPoints(): Integer
  + setPassword(pw: String)
  + checkPW(pw: String): Boolean
}
@enduml

属性声明语法

[可见性] 名称[多重性]: 类型 [= 默认值] {属性}

PlantUML示例:

@startuml
class Student {
  + ProgramOfStudy[0..2]: String = "MIS"
  - encryptedPW: String {frozen}
  # internalID: Integer
  ~ packagePrivateData: String
}
@enduml

属性作用域

  • 实例作用域 (默认): 每个对象都有其自身的值

  • 类作用域 (静态): 所有实例共享的单一值

@startuml
class Student {
  name: String
  {static} count: Integer
}
@enduml

UML 中的键 ⚠️

重要限制: UML 没有内置的键概念。可使用构造型或标记值作为变通方法。

@startuml
class Student {
  {pk} id: Integer
  {ak1} email: String
  - name: String
}
@enduml


🔹 关联与关系

基本关联与多重性

@startuml
class Exercise
class Chapter
Exercise "0..*" -- "1..1" Chapter : 属于
@enduml

解释: 每个练习恰好属于一个章节;一个章节可以包含零个或多个练习。

角色名称

除了(或除了)关联名称外,可在关联端使用角色名称:

@startuml
class Person
class Company
Person "0..*" --> "0..1" Company : 员工/雇主
@enduml

实现: 这个 人员 表将有一个外键 雇主 引用 公司.

可导航性

使用箭头指定遍历方向:

@startuml
class Exercise
class Chapter
Exercise "0..*" --> "1" Chapter
@enduml

  • 箭头表示高效遍历方向

  • 在 OODB 中:仅以单向指针实现

  • 在 RDBMS 中:连接无论哪个方向都有效

具有 的集合类型{有序}

@startuml
class Chapter
class Exercise
Chapter "1" -- "0..*" Exercise : {ordered}
@enduml

  • {有序}: 维持顺序(使用列表,而非集合)

  • 在 RDBMS 中的实现:添加序列号属性

EXERCISES (
    id 主键,
    chapter_id 引用 CHAPTERS,
    sort_no 整数,
    (chapter_id, sort_no) 唯一
)

限定符

限定符使用类似键的机制对相关对象进行分区:

@startuml
class Chapter
class Exercise
Chapter "1" --> "0..1" Exercise : <<限定符>> no: Integer
@enduml

含义: 给定一个章节和一个练习编号,最多返回一个 Exercise。

关联类

当一个关联具有属性或操作时:

@startuml
class Student
class Exercise
class Solution {
  date: Date
  points: Integer
}
Student "0..*" -- "0..*" Exercise : 已解决
Solution .. Student
Solution .. Exercise
@enduml

  • 一个 Solution 对象对应一个(学生,练习)组合

  • 强制执行:同一个学生不能为同一练习提交两个解决方案

组合与聚合

特征 组合(*--) 聚合(o--)
符号 黑色菱形 白色菱形
关系 整体-部分,强拥有关系 整体-部分,弱引用
生命周期 整体删除时,部件一同删除 部件独立
多重性 整体侧为 1 或 0..1 任意
RDBMS 映射 ON DELETE CASCADE 标准外键
@startuml
class Chapter
class Exercise
Chapter *-- "0..*" Exercise : 组成
Chapter o-- "0..*" Exercise : 聚合
@enduml


🔹 操作和方法

操作声明语法

@startuml
class Calculator {
  + getTotal(studID: Integer, inclExtra: Boolean = true): Float {isQuery=true}
  + {static} getInstance(): Calculator
  + {constructor} Calculator(initialValue: Float)
  - recalculate(): void
}
@enduml

参数规范:

[方向] 名称: 类型 [= 默认值]
  • 方向:in(默认),outinout

  • 默认值使参数可选

特殊操作构造型

构造型 目的
{isQuery=true} 保证不会修改状态
{构造函数} 创建并初始化新实例
{静态} 类级别操作,无隐式self

数据库上下文中的操作

文化冲突: 面向对象强调封装;关系型强调直接数据访问。

实现策略:

操作类型 RDBMS 实现
简单属性访问 直接 SELECT/UPDATE
派生属性(无参数) 数据库视图
派生属性(带参数) 存储过程或应用逻辑
复杂约束强制执行 触发器或应用过程

🔹 泛化与继承

基本泛化

@startuml
class Person
class Student
class Professor
Person <|-- Student
Person <|-- Professor
@enduml

抽象类和操作

@startuml
抽象类 Account {
  - balance: 浮点数
  + deposit(amount: 浮点数): void
  + {抽象} withdraw(amount: 浮点数): void
}
@enduml

泛化约束

@startuml
class Person
class Student
class Professor
class OtherPerson
Person <|-- Student : <<{互斥, 完整}>>
Person <|-- Professor : <<{互斥, 完整}>>
Person <|-- OtherPerson : <<{互斥, 完整}>>
@enduml

多重分类 / 判别器

@startuml
class Employee
class Staff
class Faculty
class HMO
class NonHMO
Employee <|-- Staff : <<类型>>
Employee <|-- Faculty : <<类型>>
Employee <|-- HMO : <<保险>>
Employee <|-- NonHMO : <<保险>>
@enduml

  • 判别器将互斥的特化分组

  • 对象在每个判别器维度上只能有一个值


🔹 扩展机制

UML 提供了三种可扩展性机制:

1. 构造型<< >>

通过创建元模型元素的新“子类型”来扩展 UML 语义。

@startuml
class Customer <<实体>> {
  - id: 整数
  - name: 字符串
}
class MathLibrary <<工具>> {
  + sin(x: 浮点数): 浮点数
  + cos(x: 浮点数): 浮点数
}
@enduml

2. 标记值{键=值}

向模型元素添加自定义属性。

@startuml
class Student {
  {author=sb, version=1.0, persistence=persistent}
  - id: Integer
}
@enduml

3. 约束{...}

使用自由文本、OCL 或预定义缩写添加语义限制。

@startuml
class Exercise {
  - no: Integer
  - points: Integer {value >= 0}
  {points <= maxPoints}
}
@enduml


🔹 面向数据库设计的 UML:关键考虑因素

将 UML 转换为关系模式

UML 构造 关系实现
属性
主键{pk} PRIMARY KEY 约束
关联 (1:*) “多”端的外键
关联 (:) 连接/交集表
组合 外键 +ON DELETE CASCADE
关联类 包含复合外键和属性的表
泛化 分开的表(带外键)或单个表加类型鉴别器
{有序}关联 添加序列列 + 唯一性约束
限定符 复合键或索引列的一部分

关键差异:面向对象 vs. 关系型

方面 面向对象 关系型
标识 对象引用(代理) 主键(业务键或代理键)
操作 设计核心,封装 外部(SQL,存储过程)
封装 私有属性,公共接口 默认直接访问表
继承 原生语言支持 复杂的映射策略
关系 指针/引用 外键和连接

数据库设计者的实用建议

  1. 显式建模键: 使用 {主键}{别名1} 构造型,因为UML不原生支持键

  2. 标记持久性: 使用 {持久化} 标记值,用于区分数据库类和临时应用类

  3. 简化操作: 将查询操作映射到视图;将复杂操作映射到存储过程

  4. 谨慎处理继承: 根据查询模式选择映射策略

  5. 记录约束: 对业务规则使用OCL或清晰文本约束

  6. 谨慎使用关联类: 仅当关系具有重要属性时


🎯 快速参考速查表

PlantUML 类图符号概要

@startuml
class <<构造型>> 类名 {
  {标记=值}
  [+/-/#/~] 名称[多重性]: 类型 [= 值] {属性}
  [+/-/#/~] 名称(参数): 返回值 {属性}
}
@enduml

关联符号

@startuml
类A "多重性A" -- "多重性B" 类B : 关联名称
类A *-- 类B  ' 组合
类A o-- 类B  ' 聚合
类A --> 类B  ' 可导航
@enduml

可见性符号

  • +公共

  • -私有

  • #受保护

  • ~

通用属性与约束

  • {静态} / {isQuery=true} / {抽象}

  • {值 >= 0} / {异或} / {有序} / {主键}


💡 最终思考: UML类图在概念建模方面非常强大,但请记住它们主要是为软件工程设计的。在使用UML进行数据库设计时,应准备好通过扩展符号(使用构造型、标记值、约束)来捕捉关系概念,如键、规范化和声明性约束,这些概念并非UML面向对象基础的原生部分。

本指南整理自Stefan Brass于2003年在哈雷大学所著的《第6部分:UML类图》。所有图表均采用PlantUML语法格式,以确保与现代工具的兼容性。

Leave a Reply