从‘大雁与雁群’到代码:用Enterprise Architect 15图解UML聚合与组合关系(Python实战)
在软件工程领域,UML类图是表达系统设计的通用语言。而聚合(Aggregation)与组合(Composition)这对"孪生概念",常常让开发者陷入"看似相似实则不同"的困惑。就像大雁可以离开雁群生存,但翅膀无法脱离大雁独立存在——这种自然界中的"整体-部分"关系,恰好完美诠释了两种关联的本质差异。
本文将带您使用Enterprise Architect 15(以下简称EA)这款专业建模工具,通过可视化建模→代码生成的完整工作流,揭示这两种关系在Python实现中的具体差异。无论您是在设计微服务架构中的模块依赖,还是构建领域模型中的复杂关联,掌握这种区分能力都将显著提升您的设计表达能力。
1. 环境准备与基础配置
1.1 EA 15的Python支持配置
安装EA后首次创建项目时,语言设置就像种下一颗种子——它决定了最终生成的代码类型。许多开发者遇到的"为什么生成的总是Java代码"问题,往往源于多层级的语言配置未同步:
<!-- 项目结构示例 --> <Project> <Package language="Python"> <Class language="Java"/> <!-- 这里埋下了隐患 --> <Diagram language="Python"/> </Package> </Project>必须检查的三级配置:
- 项目根节点:
右键 → Properties → Code Generation → Default Language - 每个包节点:
右键 → Properties → Properties → Language - 每个类节点:
右键 → Properties → Language
提示:建议创建项目后立即执行"批量修改语言"操作:
Project → Model → Tools → Default Languages...
1.2 类图基础操作速览
EA的组件工具栏采用可折叠设计(容易被忽略的>>图标),包含UML2.5标准中的所有元素。创建类图时推荐按以下顺序操作:
- 新建包(命名空间隔离)
- 添加类图视图(
右键包 → Add Diagram → UML Structural → Class) - 拖拽类元素到画布
- 使用连接器建立关系
常用快捷键速查表:
| 操作 | Windows快捷键 | Mac快捷键 |
|---|---|---|
| 快速添加属性 | Ctrl+Shift+A | Command+Shift+A |
| 显示组件工具栏 | Alt+3 | Option+3 |
| 生成代码 | F11 | Fn+F11 |
2. 聚合与组合的EA建模实践
2.1 从自然现象到UML元素
让我们用EA绘制两个经典案例:
案例一:雁群系统(聚合关系)
- 创建
Goose类和Flock类 - 选择"聚合"连接器(空心菱形)
- 将菱形端指向
Flock,箭头端指向Goose - 设置多重度为
1..*(一个雁群包含多只大雁)
# 生成的Python代码框架 class Flock: def __init__(self): self.members = [] # 可动态增减的聚合集合 class Goose: def join_flock(self, flock: Flock): flock.members.append(self)案例二:鸟类解剖(组合关系)
- 创建
Bird类和Wing类 - 选择"组合"连接器(实心菱形)
- 将菱形端指向
Bird,箭头端指向Wing - 设置多重度为
2(固定数量的组合部件)
# 生成的Python代码差异点 class Bird: def __init__(self): self.wings = (Wing(), Wing()) # 生命周期绑定的固定部件 class Wing: pass2.2 EA关系设置的深层配置
双击关系线打开属性窗口,这些配置项直接影响代码生成:
关键选项卡对比:
| 配置项 | 聚合关系建议值 | 组合关系建议值 |
|---|---|---|
| Role → Name | member | component |
| Role → Cardinality | 0..* | 固定值(如2) |
| Source → Containment | Shared | Composite |
注意:在
Target → Navigable取消勾选时,生成的Python代码将不会包含反向引用属性
3. Python代码生成深度解析
3.1 两种关系的实现差异
当EA将UML转换为Python代码时,聚合与组合的核心区别体现在三个维度:
内存管理方式:
- 聚合:使用弱引用(
weakref模块)避免循环引用 - 组合:直接强引用,父对象负责子对象生命周期
序列化表现:
import pickle # 聚合关系序列化时,外部对象需要单独处理 flock = Flock() goose = Goose() flock.members.append(goose) # 组合关系序列化时,整体自动包含部分 bird = Bird() serialized = pickle.dumps(bird) # 自动包含wings3.2 自定义代码生成模板
EA允许通过Settings → Code Generation Templates修改代码样式。例如修改组合关系的初始化方式:
- 定位到
Python语言模板 - 编辑
Attribute Declaration模板:
%if attContainment == "composite"% self.%attName% = %attType%() %endif%- 添加类型提示支持(Python 3.9+):
%if attType != ""% %attName%: %attType% %endif%4. 实战:飞机控制系统建模
让我们用更复杂的案例验证这两种关系的应用。假设我们要设计一个航空管制系统:
4.1 模型构建步骤
- 创建
Airport、Runway、ControlTower等核心类 - 建立关键关系:
- 机场与跑道:组合(跑道不能独立存在)
- 机场与航空公司:聚合(航空公司可跨机场运营)
- 设置接口
INavigable实现多态
EA操作技巧:
- 使用
F5快速添加接口实现 Ctrl+拖动复制保持关系连接- 右键菜单
Insert Related Elements快速构建关联类
4.2 生成的Python架构亮点
class Airport: def __init__(self): self.runways = [] # 组合关系 self.airlines = [] # 聚合关系 def add_runway(self, length: float): """组合关系的典型方法""" self.runways.append(Runway(length)) class Airline: def operate_at(self, airport: Airport): """聚合关系的典型方法""" airport.airlines.append(self)在调试这类系统时,组合关系的对象会随父对象一起出现在调试器中,而聚合关系需要单独检查。EA的Debug View模式(View → Debug)可以直观显示这种差异。