刚入行那会儿,我最怕的就是接到"设计个用户管理系统"这类需求。面对空白的IDE,经常陷入两种极端:要么直接开写代码导致后期改到崩溃,要么画一堆零散的方框最后自己都看不懂。直到 mentor 扔给我一本《UML精粹》,才明白类图就是解决这种困境的"设计蓝图"。
类图本质上是用图形化的方式描述系统中各个类的静态结构。就像建筑工地的施工图,它明确标注了"承重墙"(核心类)、"管道走向"(类关系)和"房间功能"(方法属性)。最近给电商平台做权限系统时,我们先用类图梳理出User、Role、Permission三个核心类,再确定它们之间的关联关系,最终编码阶段节省了至少40%的返工时间。
在权限管理系统中,User类的标准表示是这样的:
java复制// 对应UML中的类表示
public class User {
// 属性区
private Long id;
private String username;
private String encryptedPassword;
// 操作区
public boolean verifyPassword(String input) {...}
public void assignRole(Role role) {...}
}
斜体提示:属性和操作前的可见性符号(+ public, - private, # protected)是团队协作时的关键约定,我曾经因为漏写这个导致同事误用了内部方法。
去年重构旧系统时,就踩过混淆聚合与组合的坑。原来的设计把Department和Employee做成组合关系(实心菱形),结果分支机构撤销时所有员工数据都被误删。正确做法应该是空心菱形的聚合关系:
code复制Department ◇—— Employee
关系类型速查表:
| 关系类型 | 箭头样式 | 代码表现 | 生命周期 |
|---|---|---|---|
| 关联 | 普通实线 | 成员变量 | 独立 |
| 聚合 | 空心菱形实线 | 构造函数传入 | 独立 |
| 组合 | 实心菱形实线 | new在构造函数内 | 同步 |
| 依赖 | 虚线箭头 | 方法参数/局部变量 | 临时 |
接到"设计权限系统"需求时,我习惯先用名词分析法找出候选类:
经过合并筛选,确定初始类图框架:
code复制[User]
[Role]
[Permission]
[Menu]
初期设计时最容易犯的两个错误:
code复制User ◇—— Role
code复制User "1" — "0..*" Role
这是经过三次迭代后的稳定版本:
java复制// 对应完整类图实现
public class Permission {
private String code;
private String description;
}
public class Role {
private String name;
private Set<Permission> permissions = new HashSet<>();
public void addPermission(Permission p) {...}
}
public class User {
private String username;
private Set<Role> roles = new HashSet<>();
public boolean hasPermission(String permCode) {
return roles.stream().anyMatch(r ->
r.getPermissions().contains(permCode));
}
}
加粗提醒*:权限校验方法放在User类而非Role类,这符合"信息专家"设计原则——谁拥有数据谁负责操作。
使用IntelliJ IDEA的Diagrams功能可以直接从类图生成代码骨架:
实测时发现个小技巧:先在类图中定义好泛型关系,比如List<Permission>,生成的代码会包含完整类型声明。
对遗留系统进行重构时,可以用Eclipse的Model插件:
bash复制# 安装EMF插件后执行
$ eclipse -application org.eclipse.emf.codegen.ecore.Generator -model ./src/model.genmodel
最近用这个工具分析一个10年老项目,发现本应是组合关系的Order和OrderItem被实现成了数据库关联,导致大量级联删除的逻辑漏洞。
在GitLab的Merge Request中集成类图检查:
这套流程帮我们团队避免了多次接口定义冲突,特别在微服务拆分时,清晰的类图边界使服务划分更加合理。