设计模式-中介者模式

66 阅读4分钟

写在前面

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<IntegergetRolesByUser(int userId) {
        return userRoleMap.getOrDefault(userId, new ArrayList<>());
    }

    public void setRoleMenus(int roleId, List<Integer> menus) {
        roleMenuMap.put(roleId, new ArrayList<>(menus));
    }

    public List<IntegergetMenusByRole(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<IntegergetExistingMenus() {
        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<IntegergetExistingRoles() {
        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<IntegergetExistingUsers() {
        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);
    }

    // 实现接口方法
    @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);
        }
    }
}

核心机制

  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
    • 预防方案:严格单向依赖(组件 -> 中介者)