中介模式的中文翻译非常形象。大家买房子要中介,中介做的什么事情呢?我们先不会回答这个问题。而是反问依据,如果没有中介,我们买房子的场景会是怎样?
- 满世界找有可能买房子的房东?可能同时要跟20个房东打交道,跟房东约时间可真是看天气、看运气的事情
- 房产交易要签合同?我们要跟房东拟定合同?还要一起房地产交易中心做网签备案
- 如果全款购房的话,还稍微简单点。如果钱不够想分期怎么办?购房者还要跟银行打交道,可能还不止一家银行,因为各家银行的放款条件,贷款额度、贷款利率和贷款期限可能有所不同
其实上面所说的还可能只是其中一角,这也是为什么绝大部分人都选择中介来帮忙购房或中介来托管房屋,进行售房。有了中介,世界就变得如此简单,比如对于购房者而言,只要关注:
- 告诉中介我要买房,告之购房要求、可用时间,然后等着看房就好了
- 房子看重了,要贷款,去中介那里,带上身份证,签个合同就结束了
当然,对于售房者而言,也是一样的简单。因为中介为我们屏蔽了购房过程中的所有角色:购房者 * 售房者 * 银行 * 房地产交易中心,每个角色关注自己的诉求和需要做的事情即可。
这就是中介者模式的魅力。还有很多这样的例子可以拿来说说,比如:
- 机场的航站塔台对飞机的调度,飞机之间不需要互相通信
- 一个页面上的复杂表单可能有几十个组件:TextInput/Checkbox/Button/Select/Radio,通常他们是不需要关注彼此的,只要将变更告知整个表单组件以及接受表单组件的通知即可
解决问题&适用场景:
从上面的例子,我们能够看到,中介模式很实用在有多个角色之间,交互复杂且混乱的场景下使用。中介模式能够把网状的交互,编程星形的交互。
代码结构&示例
结构其实比较简单,几个关键的类:
- Component:需要进行解耦的对象,上图各个Component最好继承自一个基类,这样会便于中介类做一些抽象和统一管控;每个Component都持有中介对象的引用,有事情发生时,调用notify就好,参数就是Component对象自己就好
- Meditator:中介,一般来讲中介类只有一个notify方法就足够了。中介对象持有所有Component的引用,他会收到各个Component对象发来的通知(notify),他知道该怎么协调,下一步该做什么。某种角度,它就是“上帝”,至于说怎么让上帝不至于变得过于复杂,是另外一个需要讨论的命题
- ConcreteMediator:具体的中介实现,不同的背景和目的下,实现也会有所不同,比如上面的表单例子中,不同的表单,需要调度的组件、校验、联动规则、提交后的去向等,都会有所不同
代码上我们就以上面的星形图来做示例:
- Component
// abstract Component
public abstract class Colleague {
public abstract void onEvent(Mediator mediator);
}
// Component Alarm
public class Alarm extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("alarm");
}
public void doAlarm() {
System.out.println("doAlarm()");
}
}
// Component CoffeePot
public class CoffeePot extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("coffeePot");
}
public void doCoffeePot() {
System.out.println("doCoffeePot()");
}
}
// Component Calender
public class Calender extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("calender");
}
public void doCalender() {
System.out.println("doCalender()");
}
}
// Component Sprinkler
public class Sprinkler extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("sprinkler");
}
public void doSprinkler() {
System.out.println("doSprinkler()");
}
}
- Mediator
// Mediator
public abstract class Mediator {
public abstract void doEvent(String eventType);
}
// ConcreteMediator
public class ConcreteMediator extends Mediator {
private Alarm alarm;
private CoffeePot coffeePot;
private Calender calender;
private Sprinkler sprinkler;
public ConcreteMediator(Alarm alarm, CoffeePot coffeePot, Calender calender, Sprinkler sprinkler) {
this.alarm = alarm;
this.coffeePot = coffeePot;
this.calender = calender;
this.sprinkler = sprinkler;
}
@Override
public void doEvent(String eventType) {
switch (eventType) {
case "alarm":
doAlarmEvent();
break;
case "coffeePot":
doCoffeePotEvent();
break;
case "calender":
doCalenderEvent();
break;
default:
doSprinklerEvent();
}
}
public void doAlarmEvent() {
alarm.doAlarm();
coffeePot.doCoffeePot();
calender.doCalender();
sprinkler.doSprinkler();
}
public void doCoffeePotEvent() {
// ...
}
public void doCalenderEvent() {
// ...
}
public void doSprinklerEvent() {
// ...
}
}
- Client
public class Client {
public static void main(String[] args) {
Alarm alarm = new Alarm();
CoffeePot coffeePot = new CoffeePot();
Calender calender = new Calender();
Sprinkler sprinkler = new Sprinkler();
Mediator mediator = new ConcreteMediator(alarm, coffeePot, calender, sprinkler);
// 闹钟事件到达,调用中介者就可以操作相关对象
alarm.onEvent(mediator);
}
}
输出:
doAlarm()
doCoffeePot()
doCalender()
doSprinkler()
优缺点
Good
- 遵守单一职责原则,各个组件的职责都可以保持克制和单一
- 遵守开闭原则,各个组件的代码不需要修改,但是他们的行为的可以通过实现中介类来做扩展
- 遵守迪米特法则,各个组件之间无需知晓对方的知识,只需要关注和中介的基础交互即可
Bad
- 组件的可扩展、可维护,是将复杂度转移到中介对象上为代价,因此如果不加控制和设计,中介对象很容已成为一个“上帝对象”,啥都要管,谁都不敢改动
结语
中介模式其实是一个很流行的模式,从它遵守的几个原则就窥见一斑。在该模式下,组件的扩展和维护变得简单,但需要注意的是,要有一个真正的专家(我说的就是个人)来做中介的设计和统筹,并且为了这个专家的知识成为黑洞,甚至公司的负资产,一定要做好文档的记录和backup的培养,要不专家一走,就马上成为一坨可怕的屎山了🤮