如果您有租房的经历,那么您对中介并不陌生。租房时,我们先去房屋租赁中介登记,告诉它您的租房需求,然后中介会按照您的要求为您筛选适合的房子。房子确定后,您会与中介签订租赁合同,缴纳费用,然后拿到钥匙……整个过程中,所有事项都由中介一手包办,入住以后房屋有任何问题,您都直接去找中介,您甚至可能并不知道房东是谁。这就是我们今天要说的模式——中介者模式。
中介者模式来源于生活中的各个中介机构,不如前边提到的房产中介、贷款中介、票务中介等等。
1. 概念
DP 对中介者模式的定义是这样的:用一个 中介对象 来封装一系列的对象交互,各个对象 不显式地相互引用,从而使其 松散耦合,而且可以独立的改变它们之间的交互。这完全符合 迪米特法则。
简而言之,中介者模式将多个相互引用的对象解耦,使得它们不直接交互,而是"有事找中介"。
除了生活中的中介机构外,中介者模式在软件中应用也很多,比如,服务网关,各个服务不直接通信,而是都通过网关来互相调用;又比如,MVC 模式中,service 层就是使用典型的中介者模式,因为 service 层依赖了多个 dao 层的类,而 dao 层的类之间并不直接交互……
2. 结构
中介者模式的类图如下:
存在4个角色:
Mediator: 抽象中介者,定义了通用接口让同事类之间进行通信
ConcreteMediator: 具体中介者,实现 Mediator 的具体业务来支持同事类之间的通信
Colleague: 抽象同事类,定义同事类通用接口,内部聚合 Mediator ,通过它与其他同事类交互
ConcreteColleague: 具体同事类,调用 Mediator 实现与其他具体同事类的通信
在中介者模式中,同事类之间互不认识,但是他们都认识中介者,就好比租房时您不认识房东,但是房东和您都认识中介,通过中介来完成通信,因此,中介者是作为一个中心化的角色存在。
优点:
同事类之间完全结构,符合 迪米特法则]
同事类可以扩展和复用,并且减少了它们的子类数量
简化了对象模型,由多对多关系变成了一对多关系
中介者决定同事类之间如何交互,而抽象中介者对这种交互进行了抽象,便于扩展
缺点:
同事类之间的交互完全由中介者来控制,中介者复杂度增加
中心化的中介者出现问题,则同事类之间无法交互,即存在单点问题
3. 适用场景
中介者模式适用于以下场景:
多个对象间存在复杂的依赖关系,结构混乱且难以理解
一个对象引用其他多个对象,并直接与它们通信,导致难以复用该对象
一个对象被多个对象引用,难以扩展,又不想为其生成子类
4. 与其他模式的区别
与观察者模式
上一篇 讲了观察者模式,与中介者模式非常类似。
结构类似,都定义了类似的4个角色,两个抽象角色和两个具体角色
两者实现的功能不同,观察者模式适用于一个对象改变而其他对象也需要做出改变的场景,而中介者模式重点在于解耦对象间多对多的复杂通信关系
中介者模式可以与观察者模式组合使用,如:中介者也作为通知发送者
与外观模式
中介者模式与 外观模式 也类似:
都尝试在多个紧密耦合的类之间组织协作关系
外观模式是一种结构性模式,而中介者模式是一种行为型模式
外观模式只是简单的将各个子系统的接口进行组合再提供新的接口,本身并不实现额外的功能,子系统间可以进行直接通信;而中介者组合同事类,提供额外的功能以完成同事类间的通信,同事类之间不直接通信
5. 示例代码
来看看中介者模式的示例代码:
1、定义抽象同事类:
public interface Colleague {
void sendMessage(String msg); (1)
void receiveMessage(String msg); (2)
void setMediator(Mediator mediator); (3)
Mediator getMediator(); (4)
}
1 | 向其他同事发送消息 |
2 | 接收其他同事发来的消息 |
3 | 设置中介者对象 |
4 | 获取中介者对象 |
2、定义中介者
public interface Mediator {
void sendMessage(Colleague colleague, String msg); (1)
}
1 | 转发某个同事类发来的消息到另一个同事类 |
3、定义具体同事类
public class ConcreteColleague1 implements Colleague {
private Mediator mediator;
public ConcreteColleague1(Mediator mediator) {
this.mediator = mediator;
}
@Override
public void sendMessage(String msg) {
System.out.println(this.getClass().getSimpleName() + " 发送了消息: " + msg);
this.mediator.sendMessage(this, msg); (1)
}
@Override
public void receiveMessage(String msg) {
System.out.println(this.getClass().getSimpleName() + " 收到了消息: " + msg);
}
@Override
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
@Override
public Mediator getMediator() {
return this.mediator;
}
}
1 | 将消息发送的逻辑委托给中介者执行 |
具体同事类2的代码类似,就不贴出来了,有兴趣可以看文末源码。
两个具体同事类完成功能很简单:将发送消息的逻辑委托给中介者来执行,并打印谁发送的什么消息,还接收其他人发送来的消息。
4、具体中介者
public class ConcreteMediator implements Mediator {
private ConcreteColleague1 colleague1; (1)
private ConcreteColleague2 colleague2;
@Override
public void sendMessage(Colleague colleague, String msg) {
if (colleague == colleague1) { (2)
colleague2.receiveMessage(msg);
} else if (colleague == colleague2) {
colleague1.receiveMessage(msg);
}
}
public void setColleague1(ConcreteColleague1 colleague1) {
this.colleague1 = colleague1;
}
public void setColleague2(ConcreteColleague2 colleague2) {
this.colleague2 = colleague2;
}
}
1 | 聚合两个同事类 |
2 | 将消息发送给另一个同事类 |
5、客户端代码
public class MediatorClient {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.sendMessage("我是同事1");
colleague2.sendMessage("我是同事2");
}
}
执行后输出:
ConcreteColleague1 发送了消息: 我是同事1
ConcreteColleague2 收到了消息: 我是同事1
ConcreteColleague2 发送了消息: 我是同事2
ConcreteColleague1 收到了消息: 我是同事2
可以看到,整个交互过程,两个同事类没有直接交互,而都是通过中介者来完成的。
6. 总结
中介者模式将多对多的关系依赖关系解耦,并形成一对多的关系,完全遵循迪米特法则,中介者和同事类都可以独立扩展可复用,但是,中介者作为一个中心化的存在,功能太多,最后可以难以维护,或者导致单点问题。
另外,中介者模式、外观模式、观察者模式之间存在一定的相似性,使用时需要注意区分。
本文示例代码见: github。