写在前面
Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!
一、背景
在传统用户-角色-菜单权限系统中,存在典型的循环依赖问题:
- 用户服务需要调用角色服务接口
- 角色服务又需要调用用户服务接口
- 三个服务间形成
A<->B<->C
的网状依赖结构
导致系统出现以下问题:
- 代码耦合度高,维护困难
- 系统复杂度呈指数级增长
- 变更可能引发级联错误
二、解决方案:中介者模式
2.1 模式定义
核心思想:通过中间层管理复杂交互,将 N*(N-1)
的交互复杂度降为 N*1
,符合迪米特法则(最少知识原则)
角色定义:
角色 | 职责说明 | 本系统对应实现 |
---|---|---|
Mediator | 定义组件交互协议 | SystemMediator 接口 |
ConcreteMediator | 实现协调逻辑的中介者 | PermissionSystemMediator |
Colleague | 需要交互的业务组件 | User/Role/MenuManager |
2.2 架构转型示意图
原始结构:
用户服务 ↔ 角色服务 ↔ 菜单服务
优化后结构:
↗ 用户服务
中介者 →→→ 角色服务
↘ 菜单服务
三、代码实现解析
3.1 核心组件关系
3.2 关键组件说明
3.2.1 数据存储中心(AssociationStorage)
public class AssociationStorage {
private final Map<Integer, List<Integer>> userRoleMap = new HashMap<>();
private final Map<Integer, List<Integer>> roleMenuMap = new HashMap<>();
public void addUserRole(int userId, int roleId) {
userRoleMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(roleId);
}
public void removeUserRole(int userId, int roleId) {
List<Integer> roles = userRoleMap.get(userId);
if (roles != null) {
roles.remove(Integer.valueOf(roleId));
if (roles.isEmpty()) userRoleMap.remove(userId);
}
}
public List<Integer> getRolesByUser(int userId) {
return userRoleMap.getOrDefault(userId, new ArrayList<>());
}
public void setRoleMenus(int roleId, List<Integer> menus) {
roleMenuMap.put(roleId, new ArrayList<>(menus));
}
public List<Integer> getMenusByRole(int roleId) {
return roleMenuMap.getOrDefault(roleId, new ArrayList<>());
}
public void removeRole(int roleId) {
roleMenuMap.remove(roleId);
userRoleMap.values().forEach(roles -> roles.removeIf(id -> id == roleId));
}
public void removeMenu(int menuId) {
roleMenuMap.values().forEach(menus -> menus.removeIf(id -> id == menuId));
}
}
核心功能:
- 用户-角色映射管理
- 角色-菜单映射管理
- 自动清理无效关联(删除角色时清理用户关联,删除菜单时清理角色配置)
3.2.2 业务组件类
public class MenuManager {
private SystemMediator mediator;
public void setMediator(SystemMediator mediator) {
this.mediator = mediator;
}
private final Set<Integer> existingMenus = new HashSet<>();
public void addMenu(int menuId) {
if (existingMenus.contains(menuId)) {
throw new IllegalArgumentException("菜单已存在");
}
existingMenus.add(menuId);
}
public void deleteMenu(int menuId) {
existingMenus.remove(menuId);
mediator.notifyComponentDeleted("Menu", menuId);
}
public Set<Integer> getExistingMenus() {
return Collections.unmodifiableSet(existingMenus);
}
}
public class RoleManager {
private SystemMediator mediator;
public void setMediator(SystemMediator mediator) {
this.mediator = mediator;
}
private final Set<Integer> existingRoles = new HashSet<>();
public void addRole(int roleId) {
if (existingRoles.contains(roleId)) {
throw new IllegalArgumentException("角色已存在");
}
existingRoles.add(roleId);
}
public void deleteRole(int roleId) {
existingRoles.remove(roleId);
mediator.notifyComponentDeleted("Role", roleId);
}
public void setMenusForRole(int roleId, List<Integer> menus) {
if (!existingRoles.contains(roleId)) {
throw new IllegalArgumentException("角色不存在");
}
mediator.setRoleMenus(roleId, menus);
}
public Set<Integer> getExistingRoles() {
return Collections.unmodifiableSet(existingRoles);
}
}
public class UserManager {
private SystemMediator mediator;
public void setMediator(SystemMediator mediator) {
this.mediator = mediator;
}
private final Set<Integer> existingUsers = new HashSet<>();
public void addUser(int userId) {
if (existingUsers.contains(userId)){
throw new IllegalArgumentException("用户已存在: " + userId);
}
existingUsers.add(userId);
}
public void deleteUser(int userId) {
existingUsers.remove(userId);
mediator.notifyComponentDeleted("User", userId);
}
public Set<Integer> getExistingUsers() {
return Collections.unmodifiableSet(existingUsers);
}
public void assignRole(int userId, int roleId) {
if (!existingUsers.contains(userId)){
throw new IllegalArgumentException("无效用户ID");
}
mediator.linkUserToRole(userId, roleId);
}
}
统一行为:
- 通过
setMediator
注入中介者 - 组件变更时通过中介者广播通知
- 业务操作委托中介者执行
3.2.3 中介者实现(PermissionSystemMediator)
public interface SystemMediator {
// 关联操作
void linkUserToRole(int userId, int roleId) throws IllegalArgumentException;
void unlinkUserFromRole(int userId, int roleId);
void setRoleMenus(int roleId, List<Integer> menus) throws IllegalArgumentException;
// 组件操作通知
void notifyComponentDeleted(String componentType, int id);
// 查询能力
boolean doesRoleExist(int roleId);
boolean doesMenuExist(int menuId);
boolean doesUserExist(int userId);
List<Integer> getMenusByRole(int roleId);
List<Integer> getRolesByUser(int userId);
}
public class PermissionSystemMediator implements SystemMediator {
private UserManager userManager;
private RoleManager roleManager;
private MenuManager menuManager;
private final AssociationStorage storage = new AssociationStorage();
public PermissionSystemMediator(UserManager userManager, RoleManager roleManager, MenuManager menuManager) {
this.userManager = userManager;
this.userManager.setMediator(this);
this.roleManager = roleManager;
this.roleManager.setMediator(this);
this.menuManager = menuManager;
this.menuManager.setMediator(this);
}
// 实现接口方法
@Override
public void linkUserToRole(int userId, int roleId) {
validateUserExists(userId);
validateRoleExists(roleId);
storage.addUserRole(userId, roleId);
}
@Override
public void unlinkUserFromRole(int userId, int roleId) {
storage.removeUserRole(userId, roleId);
}
@Override
public void setRoleMenus(int roleId, List<Integer> menus) {
validateRoleExists(roleId);
menus.forEach(this::validateMenuExists);
storage.setRoleMenus(roleId, menus);
}
@Override
public void notifyComponentDeleted(String componentType, int id) {
switch (componentType) {
case "Menu":
storage.removeMenu(id);
break;
case "Role":
storage.removeRole(id);
break;
case "User":
storage.getRolesByUser(id).forEach(roleId -> unlinkUserFromRole(id, roleId));
break;
}
}
@Override
public boolean doesRoleExist(int roleId) {
return roleManager.getExistingRoles().contains(roleId);
}
@Override
public boolean doesMenuExist(int menuId) {
return menuManager.getExistingMenus().contains(menuId);
}
@Override
public boolean doesUserExist(int userId) {
return userManager.getExistingUsers().contains(userId);
}
@Override
public List<Integer> getMenusByRole(int roleId) {
return storage.getMenusByRole(roleId);
}
@Override
public List<Integer> getRolesByUser(int userId) {
return storage.getRolesByUser(userId);
}
private void validateUserExists(int userId) {
if (!doesUserExist(userId)) {
throw new IllegalArgumentException("用户不存在: " + userId);
}
}
private void validateRoleExists(int roleId) {
if (!doesRoleExist(roleId)) {
throw new IllegalArgumentException("角色不存在: " + roleId);
}
}
private void validateMenuExists(int menuId) {
if (!doesMenuExist(menuId)) {
throw new IllegalArgumentException("菜单不存在: " + menuId);
}
}
}
核心机制:
- 关联验证机制:执行操作前验证组件存在性
- 级联清理机制:组件删除时自动清理关联
- 统一入口:所有跨组件操作通过中介者执行
四、实施路径与最佳实践
4.1 实施步骤
-
定义交互协议
- 创建
SystemMediator
接口声明核心方法 - 确定组件交互场景(关联管理、删除通知等)
- 创建
-
实现中介者
- 创建
PermissionSystemMediator
实现类 - 集成
AssociationStorage
进行数据管理 - 实现组件存在性验证等基础功能
- 创建
-
改造业务组件
// 改造前 class UserManager { private RoleService roleService; } // 改造后 class UserManager { private SystemMediator mediator; public void setMediator(SystemMediator mediator) { this.mediator = mediator; } }
-
建立关联关系
mediator.linkUserToRole(101, 201);
五、适用场景与注意事项
推荐使用场景
- 系统包含超过 3 个交互组件
- 交互规则需要动态调整
- 存在复杂的跨服务业务逻辑
- 需要集中管理权限控制策略
注意事项
-
中介者膨胀
- 监控指标:单个中介者方法超过 20 个
- 解决方案:按业务域拆分中介者
-
性能瓶颈
- 监控指标:中介者方法响应时间 > 100ms
- 优化方案:引入异步处理机制
-
循环依赖
- 检测手段:使用 Spring Boot 的
CircularReferencesDetection
- 预防方案:严格单向依赖(组件 -> 中介者)
- 检测手段:使用 Spring Boot 的