🔥 核心
减少对象之间混乱的通信,由中介者对它们进行统一的协调。
🙁 问题场景
你是一名机场管理员,最近的安全问题令你时刻提心吊胆。
事情是这样的。由于最近的天气原因,附近的许多机场已经关停,导致你所管理的机场需要降落的飞机数量急剧上升。这造成了严重的问题——飞行员需要留意机场附近的所有飞机,甚至需要与数十位飞行员共同讨论降落顺序。
即使飞行员们尽全力进行通信,你已经数次看见两架甚至三架飞机擦肩而过的场景。不能再这样下去了,必须赶快想一个办法!
🙂 解决方案
你的机场需要一座塔台。
所有飞机只需要把信息发给塔台,也只需要接受塔台的信息,而并不需要与其他的任何飞机进行通信。也就是说,塔台作为了所有飞机的「中介者」,负责整合信息甚至做出决策;而多家飞机之间,与其他飞机的交互实现了解耦,这是「迪米特法则」的典例。
🌈 有趣的例子
赌徒(Gambler)
之间有着混乱的金钱流动,如果让他们自己去处理这个问题,恐怕会经常出现冲突。
于是它们共同加入了 赌徒小屋(GamblerHouse)
,这是一个中介者。金钱流动由中介者协调,赌徒们只需要一个指向中介者的引用即可。
赌徒
class Gambler {
// 名字
private String name;
// 财产
private int money;
// 引用到中介者
GamblerHouse gamblerHouse;
public Gambler(String name, int money, GamblerHouse gamblerHouse) {
this.name = name;
this.money = money;
this.gamblerHouse = gamblerHouse;
}
public String getName() { return name; }
public int getMoney() { return money; }
public void setName(String name) { this.name = name; }
public void setMoney(int money) { this.money = money; }
// 通过该方法调用到中介者
public void changeMoney(int change) {
gamblerHouse.someoneWin(name, change);
}
}
赌徒小屋(中介者)
class GamblerHouse {
// 所有的赌徒对象集合
List<Gambler> gamblerList = new ArrayList<>();
// 赌徒通过该方法登记注册
public void register(Gambler gambler) {
gamblerList.add(gambler);
}
// 作为中介者,协调赌徒间的交互
public void someoneWin(String name, int change) {
for (Gambler gambler : gamblerList) {
if (gambler.getName().equals(name)) {
gambler.setMoney(gambler.getMoney() + change);
} else {
gambler.setMoney(gambler.getMoney() - change);
}
}
}
// 展示小屋内所有赌徒的信息
public void showEveryone() {
for (Gambler gambler : gamblerList) {
System.out.println("【" + gambler.getName() + "】" + gambler.getMoney());
}
}
}
public class MediatorPatternDemo {
public static void main(String[] args) {
// 创建一个赌徒小屋(中介者)
GamblerHouse gamblerHouse = new GamblerHouse();
// 创建多个赌徒对象
Gambler A = new Gambler("A", 10000, gamblerHouse);
Gambler B = new Gambler("B", 20000, gamblerHouse);
Gambler C = new Gambler("C", 30000, gamblerHouse);
// 赌徒在赌徒小屋进行登记
gamblerHouse.register(A);
gamblerHouse.register(B);
gamblerHouse.register(C);
// 赌徒小屋作为中介者,协调赌徒间的交互
gamblerHouse.showEveryone();
A.changeMoney(8000);
gamblerHouse.showEveryone();
}
}
【A】10000
【B】20000
【C】30000
【A】18000
【B】12000
【C】22000
☘️ 使用场景
◾️当一些对象和其他对象紧密耦合以致难以对其进行修改时,可使用中介者模式。
该模式让你将对象间的所有关系抽取成为一个单独的类,以使对于特定组件的修改工作独立于其他组件。
◾️当组件因过于依赖其他组件而无法在不同应用中复用时,可使用中介者模式。
应用中介者模式后,每个组件不再知晓其他组件的情况。尽管这些组件无法直接交流,但它们仍可通过中介者对象进行间接交流。如果你希望在不同应用中复用一个组件,则需要为其提供一个新的中介者类。
◾️如果为了能在不同情景下复用一些基本行为,导致你需要被迫创建大量组件子类时,可使用中介者模式。
由于所有组件间关系都被包含在中介者中,因此你无需修改组件就能方便地新建中介者类以定义新的组件合作方式。
🧊 实现方式
(1)找到一组当前紧密耦合,且提供其独立性能带来更大好处的类(例如更易于维护或更方便复用)。
(2)声明中介者接口并描述中介者和各种组件之间所需的交流接口。在绝大多数情况下,一个接收组件通知的方法就足够了。如果你希望在不同情景下复用组件类,那么该接口将非常重要。只要组件使用通用接口与其中介者合作,你就能将该组件与不同实现中的中介者进行连接。
(3)实现具体中介者类。该类可从自行保存其下所有组件的引用中受益。
(4)你可以更进一步,让中介者负责组件对象的创建和销毁。此后,中介者可能会与工厂或外观类似。
(5)组件必须保存对于中介者对象的引用。该连接通常在组件的构造函数中建立,该函数会将中介者对象作为参数传递。
(6)修改组件代码,使其可调用中介者的通知方法,而非其他组件的方法。然后将调用其他组件的代码抽取到中介者类中,并在中介者接收到该组件通知时执行这些代码。
🎲 优缺点
➕ 你可以更方便地复用各个组件。
➕ 你可以减轻应用中多个组件间的耦合情况。
➕ 符合迪米特法则。
➕ 单一职责原则。你可以将多个组件间的交流抽取到同一位置,使其更易于理解和维护。
➕ 开闭原则。你无需修改实际组件就能增加新的中介者。
➖ 一段时间后,中介者可能会演化成为上帝对象。
🌸 补充
中介者模式可以进一步简化,即让被协调者不再拥有指向中介者的引用,而是直接获取中介者单例。