二十三种设计模式(十八)--中介者模式

中介者模式(Mediator)

中介者模式为了解决各种子模块子组件之间相互错综复杂的依赖问题, 将所有子模块之间的依赖关系单独抽离出来封装到中介者类中, 实现子模块彼此间独立, 各司其职.

中介者类与MVC架构中的Controller类很相似, 但是中介者模式的封装类很容易就变成了一个上帝类, 内部不仅封装了模块间错综复杂的依赖关系, 还封装了业务逻辑, 业务逻辑变更了, 中介者类也变更, 违反单一职责原则.

中介者模式要注意三项职责分开:

  1. 子模块各司其职, 跟业务逻辑无关, 它们之间的交互全都通过中介者类协调
  2. 中介者类只负责解决子模块之间的依赖关系, 也跟业务逻辑无关
  3. 业务逻辑类只负责业务逻辑, 不要将其混写在中介者类或者子模块类中

假设我有三个模块如下, 以及它们之间的依赖关系描述如下

java 复制代码
// 日志模块启动前需要先启动DB模块
// 日志需要记录一部分内容到DB中
class LogModule {

}

class DBModule {

}

// 摄像头模块启动前需要启动Log模块
// 摄像头模块需要记录摄像头启动各项检测参数结果到日志中
// 摄像头模块需要记录运行内容到DB模块中
class CamModule {

}

那么接下来我就要封装一个中介者类, 来解决它们之间依赖关系,

每个模块只负责它们各自的功能实现即可

示例如下:

java 复制代码
interface Mediator {
    // 注册模块
    void registerModule(AbstractModule module);
    // 模块启动, 所有模块的启动逻辑顺序都由这里编写
    void startAll();
    // 中介请求, 模块之间的信息传递都通过这个方法进行
    void sendRequest(String moduleName, String method, String data);
}

// 所有的模块都继承这个模块抽象类
// 抽象类中定义模块间如何通过中介者类进行信息传递和方法调用
abstract class AbstractModule {
    protected Mediator mediator;
    protected String moduleName; // 模块名

    public AbstractModule(Mediator mediator, String moduleName) {
        this.mediator   = mediator;
        this.moduleName = moduleName;
    }

    public abstract void start();
    // 接收来自其他模块的请求
    public abstract void receiveRequest(String method, String data);
}


class LogModule extends AbstractModule {
    public static final String MODULE_NAME = "LogModule";

    LogModule(Mediator mediator) {
        super(mediator, MODULE_NAME);
    }

    @Override
    public void start() {
        System.out.println("[" + this.moduleName + "] start success.");
    }

    @Override
    public void receiveRequest(String method, String data) {
        if (method.equals("log")) {
            this.log(data);
        }
    }

    public void log(String data) {
        System.out.println("[log] " + data);
        // 这里需要将日志存入DB中时, 不直接new DBModule获取DB模块对象
        // 而是将数据通过中介者转发给DBModule模块
        // 这里的设计就是中介者模式的目的, 模块之间不依赖, 所有的依赖交给中介者处理
        String insertData = "[DB-log]" + data;
        mediator.sendRequest(DBModule.MODULE_NAME, "insert", insertData);
    }
}

class DBModule extends AbstractModule {
    public static final String MODULE_NAME = "DBModule";

    DBModule(Mediator mediator) {
        super(mediator, MODULE_NAME);
    }

    @Override
    public void start() {
        System.out.println("[" + this.moduleName + "] start success.");
    }

    @Override
    public void receiveRequest(String method, String data) {
        if (method.equals("insert")) {
            this.insert(data);
        }
    }

    public void insert(String data) {
        System.out.println("[insert] DB insert: " + data);
    }
}

class CamModule extends AbstractModule{
    public static final String MODULE_NAME = "CamModule";
    CamModule(Mediator mediator) {
        super(mediator, MODULE_NAME);
    }

    @Override
    public void start() {
        System.out.println("[" + this.moduleName + "] start success.");
        // 这里也通过中介调用log类记录日志, 而不是直接new LogModule
        String logData = "[Cam] init params a b c d";
        mediator.sendRequest(LogModule.MODULE_NAME, "log", logData);
    }

    @Override
    public void receiveRequest(String method, String data) {

    }

    public void run() {
        System.out.println("[cam] camera running");
        // 通过中介记录日志
        String runLog = "[cam] camera is ok";
        mediator.sendRequest(LogModule.MODULE_NAME, "log", runLog);
    }
}


// 中介者类实现
class ModuleMediator implements Mediator {
    private Map<String, AbstractModule> moduleMap = new HashMap<>();

    @Override
    public void registerModule(AbstractModule module) {
        if (!moduleMap.containsKey(module.moduleName)) {
            moduleMap.put(module.moduleName, module);
            System.out.println("[Mediator] register module: " + module.moduleName);
        }
    }

    // 这里组织所有模块的正确启动顺序
    @Override
    public void startAll() {
        DBModule dbModule = (DBModule) moduleMap.get(DBModule.MODULE_NAME);
        LogModule logModule = (LogModule) moduleMap.get(LogModule.MODULE_NAME);
        CamModule camModule = (CamModule) moduleMap.get(CamModule.MODULE_NAME);
        if (dbModule == null) {
            System.out.println("[ModuleMediator] " + DBModule.MODULE_NAME + " 模块未注册.");
            return;
        }

        if (logModule == null) {
            System.out.println("[ModuleMediator] " + LogModule.MODULE_NAME + " 模块未注册.");
            return;
        }

        if (camModule == null) {
            System.out.println("[ModuleMediator] " + CamModule.MODULE_NAME + " 模块未注册.");
            return;
        }

        dbModule.start();
        logModule.start();
        camModule.start();
    }

    // 转发模块间的各种请求
    @Override
    public void sendRequest(String moduleName, String method, String data) {
        AbstractModule module = moduleMap.get(moduleName);
        if (module != null) {
            module.receiveRequest(method, data);
        } else {
            System.out.println("[ModuleMediator] Wrong moduleName.");
        }
    }
}

客户端调用与业务逻辑

java 复制代码
import java.util.Map;
import java.util.HashMap;

public class MediatorPattern {
    public static void main(String[] args) {
        // 应用端先实例化中介者, 然后注册各个模块, 再编写业务逻辑
        System.out.println("==== 注册模块到中介 =====");
        Mediator mediator = new ModuleMediator();
        mediator.registerModule(new DBModule(mediator));
        mediator.registerModule(new LogModule(mediator));
        CamModule camModule = new CamModule(mediator);
        mediator.registerModule(camModule);

        System.out.println("==== 中介执行各模块初始化 =====");
        // 执行各模块启动逻辑
        mediator.startAll();

        System.out.println("==== 业务逻辑 =====");
        camModule.run();
    }
}

输出

复制代码
==== 注册模块到中介 =====
[Mediator] register module: DBModule
[Mediator] register module: LogModule
[Mediator] register module: CamModule
==== 中介执行各模块初始化 =====
[DBModule] start success.
[LogModule] start success.
[CamModule] start success.
[log] [Cam] init params a b c d
[insert] DB insert: [DB-log][Cam] init params a b c d
==== 业务逻辑 =====
[cam] camera running
[log] [cam] camera is ok
[insert] DB insert: [DB-log][cam] camera is ok

这个中介者模式更接近真实项目的状态, 其中的缺陷也是很明显的

  1. 模块与方法的匹配均通过字符串匹配的方式, 非常容易出错且低效, 实际应用中会用枚举+哈希表对应的方式来降低出错概率
  2. 在中介类中有startAll方法, 这里设计并不合理, 当有新的模块加入时, 这个startAll方法就必须改, 优化时需要将这部分再抽离出去

综上, 中介者模式最需要注意的点在于

  1. 模块间没有复杂的依赖关系, 不会出现一个模块中实例化另一个模块的现象
  2. 模块之间的相互方法调用统一通过中介对象转发实现

如果我的模块为了完成一件事,必须知道"另外几个模块的存在和调用顺序",那我就该考虑中介者模式。

相关推荐
带刺的坐椅8 小时前
Solon AI Skills 会是 Agent 的未来吗?
java·agent·langchain4j·solon-ai
jacGJ8 小时前
记录学习--文件读写
java·前端·学习
花间相见9 小时前
【JAVA开发】—— Nginx服务器
java·开发语言·nginx
扶苏-su9 小时前
Java---Properties 类
java·开发语言
cypking9 小时前
四、CRUD操作指南
java
2301_7806698610 小时前
文件字节流输出、文件复制、关闭流的方法
java
剑锋所指,所向披靡!11 小时前
C++之类模版
java·jvm·c++
Coder_Boy_11 小时前
基于SpringAI的在线考试系统-0到1全流程研发:DDD、TDD与CICD协同实践
java·人工智能·spring boot·架构·ddd·tdd
sheji341611 小时前
【开题答辩全过程】以 面向高校校园的物物交换系统设计与实现为例,包含答辩的问题和答案
java·eclipse
卓怡学长11 小时前
m115乐购游戏商城系统
java·前端·数据库·spring boot·spring·游戏