当前位置: 首页 > news >正文

设计模式-中介者模式

写在前面

Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!

一、背景

在传统用户-角色-菜单权限系统中,存在典型的循环依赖问题:

  • 用户服务需要调用角色服务接口

  • 角色服务又需要调用用户服务接口

  • 三个服务间形成 A<->B<->C 的网状依赖结构

导致系统出现以下问题:

  1. 代码耦合度高,维护困难

  2. 系统复杂度呈指数级增长

  3. 变更可能引发级联错误

二、解决方案:中介者模式

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));}
}

核心功能

  1. 用户-角色映射管理

  2. 角色-菜单映射管理

  3. 自动清理无效关联(删除角色时清理用户关联,删除菜单时清理角色配置)

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);}
}

统一行为

  1. 通过 setMediator 注入中介者

  2. 组件变更时通过中介者广播通知

  3. 业务操作委托中介者执行

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);}}
}

核心机制

  1. 关联验证机制:执行操作前验证组件存在性

  2. 级联清理机制:组件删除时自动清理关联

  3. 统一入口:所有跨组件操作通过中介者执行

四、实施路径与最佳实践

4.1 实施步骤

  1. 定义交互协议

    • 创建 SystemMediator 接口声明核心方法

    • 确定组件交互场景(关联管理、删除通知等)

  2. 实现中介者

    • 创建 PermissionSystemMediator 实现类

    • 集成 AssociationStorage 进行数据管理

    • 实现组件存在性验证等基础功能

  3. 改造业务组件

    // 改造前  
    class UserManager {  private RoleService roleService;  
    }  // 改造后  
    class UserManager {  private SystemMediator mediator;  public void setMediator(SystemMediator mediator) {  this.mediator = mediator;  }  
    }  
    
  4. 建立关联关系

    mediator.linkUserToRole(101, 201);  
    

五、适用场景与注意事项

推荐使用场景

  1. 系统包含超过 3 个交互组件

  2. 交互规则需要动态调整

  3. 存在复杂的跨服务业务逻辑

  4. 需要集中管理权限控制策略

注意事项

  1. 中介者膨胀

    • 监控指标:单个中介者方法超过 20 个

    • 解决方案:按业务域拆分中介者

  2. 性能瓶颈

    • 监控指标:中介者方法响应时间 > 100ms

    • 优化方案:引入异步处理机制

  3. 循环依赖

    • 检测手段:使用 Spring Boot 的 CircularReferencesDetection

    • 预防方案:严格单向依赖(组件 -> 中介者)

相关文章:

  • 研读论文《Attention Is All You Need》(4)
  • 【Oracle专栏】清理告警日志、监听日志
  • 如何创建自动工作流程拆分Google Drive中的PDF文件
  • 【kafka】kafka概念,使用技巧go示例
  • 【!!!!终极 Java 中间件实战课:从 0 到 1 构建亿级流量电商系统全链路解决方案!!!!保姆级教程---超细】
  • 试除法判断素数优化【C语言】
  • 解决docker alpine缺少字体的问题 Could not initialize class sun.awt.X11FontManager
  • 使用 Docker Desktop 安装 Neo4j 知识图谱
  • 面试--HTML
  • scikit-learn在无监督学习算法的应用
  • 网络协议分析 实验五 UDP-IPv6-DNS
  • Leetcode (力扣)做题记录 hot100(62,64,287,108)
  • Java 虚拟线程(Virtual Threads):原理、性能提升与实践
  • Vue 图片预览功能(含缩略图)
  • 5.14本日总结
  • 常见 RPC 协议类别对比
  • WEB安全--Java安全--CC1利用链
  • 如何迁移 WSL 卸载 Ubuntu WSL
  • 5.18-AI分析师
  • 【深入Spring系列】源码级深入剖析SpringBoot如何实现自动装载
  • 病重老人被要求亲自取钱在农业银行门口去世?株洲警方介入
  • 人民日报:从“轻微免罚”看涉企执法方式转变
  • 最高降九成!特朗普签署降药价行政令落地存疑,多家跨国药企股价收涨
  • 27岁杨阳拟任苏木镇党委副职,系2020年内蒙古自治区选调生
  • 普京提议无条件重启俄乌谈判,外交部:我们支持一切致力于和平的努力
  • 伊美第四轮核问题谈判开始