中介者模式(Mediator)
中介者模式为了解决各种子模块子组件之间相互错综复杂的依赖问题, 将所有子模块之间的依赖关系单独抽离出来封装到中介者类中, 实现子模块彼此间独立, 各司其职.
中介者类与MVC架构中的Controller类很相似, 但是中介者模式的封装类很容易就变成了一个上帝类, 内部不仅封装了模块间错综复杂的依赖关系, 还封装了业务逻辑, 业务逻辑变更了, 中介者类也变更, 违反单一职责原则.
中介者模式要注意三项职责分开:
- 子模块各司其职, 跟业务逻辑无关, 它们之间的交互全都通过中介者类协调
- 中介者类只负责解决子模块之间的依赖关系, 也跟业务逻辑无关
- 业务逻辑类只负责业务逻辑, 不要将其混写在中介者类或者子模块类中
假设我有三个模块如下, 以及它们之间的依赖关系描述如下
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
这个中介者模式更接近真实项目的状态, 其中的缺陷也是很明显的
- 模块与方法的匹配均通过字符串匹配的方式, 非常容易出错且低效, 实际应用中会用枚举+哈希表对应的方式来降低出错概率
- 在中介类中有startAll方法, 这里设计并不合理, 当有新的模块加入时, 这个startAll方法就必须改, 优化时需要将这部分再抽离出去
综上, 中介者模式最需要注意的点在于
- 模块间没有复杂的依赖关系, 不会出现一个模块中实例化另一个模块的现象
- 模块之间的相互方法调用统一通过中介对象转发实现
如果我的模块为了完成一件事,必须知道"另外几个模块的存在和调用顺序",那我就该考虑中介者模式。