设计模式-中介者模式
写在前面
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);}// 实现接口方法@Overridepublic void linkUserToRole(int userId, int roleId) {validateUserExists(userId);validateRoleExists(roleId);storage.addUserRole(userId, roleId);}@Overridepublic void unlinkUserFromRole(int userId, int roleId) {storage.removeUserRole(userId, roleId);}@Overridepublic void setRoleMenus(int roleId, List<Integer> menus) {validateRoleExists(roleId);menus.forEach(this::validateMenuExists);storage.setRoleMenus(roleId, menus);}@Overridepublic 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;}}@Overridepublic boolean doesRoleExist(int roleId) {return roleManager.getExistingRoles().contains(roleId);}@Overridepublic boolean doesMenuExist(int menuId) {return menuManager.getExistingMenus().contains(menuId);}@Overridepublic boolean doesUserExist(int userId) {return userManager.getExistingUsers().contains(userId);}@Overridepublic List<Integer> getMenusByRole(int roleId) {return storage.getMenusByRole(roleId);}@Overridepublic 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
-
预防方案:严格单向依赖(组件 -> 中介者)
-