什么是中介者模式(Mediator)
概念
中介者模式(Mediator Pattern)属于行为型模式,中介者模式是为了解决对象之间相互引用的情况,通过中介者对象来解除这些对象之间的相互引用,让对象之间的通信交给中介者对象来完成,使得对象从网状结构转变成了星形结构,以此降低对象间的耦合程度。
像我们在水群里聊天,我们可以把每个客户端当成一个对象,如果采用网状结构,那么就得每个客户端都得记录其他客户端的地址才行,那一个人想要加入这个水群就非常地困难,如果是采用星形结构,每个客户端都和一个服务端进行连接,服务端可以看作一个中介者,所有的客户端想要收发消息都得通过服务端,服务端会帮客户端转发消息,这样每个客户端想要加入水群,只需要找到这个服务端即可,这样就特别方便了。
优点
- 符合迪米特法则,单一职责原则。由中介者来完成和其他对象的交互,对象无需知道和其他对象交互的过程。
- 减低了类的复杂程度。将对象一对多的关系装换成了一对一的关系,也有利于系统的维护和扩展。
总之就是减少了对象之间的交集。
缺点
- 当中介者承担越来越多的责任时,会使系统变得复杂难以维护。
这也是无可厚非的,可以考虑新建其他类来降低中介者的责任。
原则
“+”代表遵守,“-”代表不遵守或者不相关
原则 | 开放封闭 | 单一职责 | 迪米特 | 里氏替换 | 依赖倒置 | 接口隔离 | 合成复用 |
---|---|---|---|---|---|---|---|
- | + | + | - | + | - | - | |
适用场景
- 对象之间呈现网状结构。
- 想要通过一个类来封装多个对象的行为。
如何实现
想要实现中介者模式,需要以下四个对象:
- 中介者抽象类/接口:定义同事对象注册与转发信息的操作。
- 中介者实现类:实现中介者抽象类/接口,定义一个集合来管理同事对象。
- 同事抽象类:定义同事类的方法,依赖中介者对象。
- 同事类:实现同事抽象类,当需要和其他同事对象交互时,调用中介者对象。
类图
同事对象中需要包含中介者,中介者中包含所有同事对象。
例子
就照着上面水群的例子吧,写一个模拟群聊的代码。
类图
代码
中介者接口
/**
* Created on 2021/6/11.
*
* @author xuxiaobai
*/
public interface Server {
/**
* 注册客户端
* @param client
*/
void register(Client client);
/**
* 服务端帮忙转发
* @param message
* @param source
*/
void relay(String message,Client source);
/**
* 注销客户端
* @param client
*/
void cancel(Client client);
}
中介者实现类
/**
* 中介者
* 服务端
* Created on 2021/6/11.
*
* @author xuxiaobai
*/
public class ChatServer implements Server{
List<Client> clients=new ArrayList();
@Override
public void register(Client client) {
clients.add(client);
}
@Override
public void relay(String message,Client source) {
System.out.println("<接收情况");
clients.forEach((item)->{
if (source.equals(item)){
return;
}
item.receiver(message);
});
System.out.println("接收情况>");
}
@Override
public void cancel(Client client) {
clients.remove(client);
}
}
同事抽象类
/**
* Created on 2021/6/11.
*
* @author xuxiaobai
*/
public abstract class Client {
protected Server server;
protected String username;
public Client(Server server,String username){
this.server=server;
this.username=username;
}
/**
* 修改服务器
* @param server
*/
public abstract void changeServer(Server server);
/**
* 接收消息
* @param msg
*/
public abstract void receiver(String msg);
/**
* 发送消息
* @param msg
*/
public abstract void send(String msg);
}
同事实现类
/**
* 同事类
* 客户端
* Created on 2021/6/11.
*
* @author xuxiaobai
*/
public class ChatClient extends Client{
public ChatClient(Server server, String username) {
super(server, username);
server.register(this);
}
public void changeServer(Server server){
super.server.cancel(this);
super.server=server;
server.register(this);
}
@Override
public void receiver(String msg) {
System.out.println(username+"接收到了消息:"+msg);
}
@Override
public void send(String msg) {
System.out.println(username+"发送消息:"+msg);
server.relay(this.username+"说"+msg,this);
}
}
测试类
/**
* 测试类
* Created on 2021/6/11.
*
* @author xuxiaobai
*/
public class MediatorTest {
public static void main(String[] args) {
Server server=new ChatServer();
//模拟客户端,加入服务器
Client clientA=new ChatClient(server,"ClientA");
Client clientB=new ChatClient(server,"ClientB");
Client clientC=new ChatClient(server,"ClientC");
//模拟通话
clientA.send("hi");
clientB.send("hello");
clientA.send("我们说中文吧。");
clientB.send("好。");
/**
* ClientA发送消息:hi
* <接收情况
* ClientB接收到了消息:ClientA说hi
* ClientC接收到了消息:ClientA说hi
* 接收情况>
* ClientB发送消息:hello
* <接收情况
* ClientA接收到了消息:ClientB说hello
* ClientC接收到了消息:ClientB说hello
* 接收情况>
* ClientA发送消息:我们说中文吧。
* <接收情况
* ClientB接收到了消息:ClientA说我们说中文吧。
* ClientC接收到了消息:ClientA说我们说中文吧。
* 接收情况>
* ClientB发送消息:好。
* <接收情况
* ClientA接收到了消息:ClientB说好。
* ClientC接收到了消息:ClientB说好。
* 接收情况>
*
*/
}
}
这里简单地模式了一下客户端之间的通信,客户端都不是直接通信的,都需要通过server来转发自己的消息,有那么一点点抽象,可以多看看。
如果想实现一个聊天室,可以去了解以下websocket协议,然后做一个聊天室,你会发现聊天室也是使用了中介者模式。
总结
中介者模式跟组合模式非常地相似,都是用一个类去管理集合,但组合模式是屏蔽掉整体组合对象和单个对象的区别,而中介者模式是解除对象间的复杂依赖。
在使用中介者模式时,需要注意类的职责划分,一旦职责没有划分明确,会提高系统的复杂程度,适得其反。