组件互相依赖到改一个崩三个?中介者模式来拆弹
我刚接手一个航班搜索模块的时候,被代码的耦合度吓到了。
航班选择器改了出发城市,要通知价格计算器重新报价;价格变了,要通知行李额度组件刷新;行李额度变了,又反过来影响价格。六个组件互相持有对方的引用,改一个组件的接口,另外五个全部编译报错。
画成依赖关系图就是一个蜘蛛网,没有任何人敢动这个模块。
这种"网状依赖"的问题,中介者模式一句话就能解决:所有组件不直接互相通信,而是通过一个中间人(中介者)来协调。
最直观的例子:机场调度塔
飞机之间不需要互相通信——A 飞机不需要知道 B 飞机的位置。所有飞机只跟调度塔说话。
```java // 中介者接口 public interface ATC { void requestTakeoff(Aircraft aircraft); void requestLanding(Aircraft aircraft); void notifyRunwayCleared(Aircraft aircraft); }
// 具体中介者 public class ControlTower implements ATC { private Queue landingQueue = new LinkedList<>(); private boolean runwayBusy = false;
@Override public void requestTakeoff(Aircraft aircraft) { if (!runwayBusy) { runwayBusy = true; aircraft.takeoff(); } else { System.out.println(aircraft + " 等待跑道"); } } @Override public void requestLanding(Aircraft aircraft) { if (!runwayBusy) { runwayBusy = true; aircraft.land(); } else { landingQueue.offer(aircraft); } } @Override public void notifyRunwayCleared(Aircraft aircraft) { runwayBusy = false; if (!landingQueue.isEmpty()) { Aircraft next = landingQueue.poll(); runwayBusy = true; next.land(); } }}
// 同事类(组件) public class Aircraft { private ATC atc; private String name;
public Aircraft(String name, ATC atc) { this.name = name; this.atc = atc; } public void requestTakeoff() { atc.requestTakeoff(this); } public void requestLanding() { atc.requestLanding(this); } public void takeoff() { System.out.println(name + " 起飞"); } public void land() { System.out.println(name + " 降落"); }} ```
你看,Aircraft 类完全不知道其他飞机的存在,它只认识 ATC(调度塔)。增删飞机不影响任何其他飞机的代码。
回到航班搜索的实际场景
把那个蜘蛛网一样的模块改造成中介者模式后,结构变成了星形:
```java public class SearchMediator { private FlightSelector flightSelector; private PriceCalculator priceCalculator; private BaggageChecker baggageChecker; private InsurancePanel insurancePanel;
public void onFlightChanged(String flightNo) { priceCalculator.recalculate(flightNo); baggageChecker.reset(flightNo); insurancePanel.update(flightNo); } public void onBaggageChanged(Baggage baggage) { priceCalculator.adjustForBaggage(baggage); } public void onPriceChanged(BigDecimal newPrice) { insurancePanel.updatePremium(newPrice); }} ```
每个组件只持有 Mediator 的引用,不直接持有其他组件。改了 BaggageChecker 的接口,只有 Mediator 需要改,其他组件完全不受影响。
你可能已经用过了
MVC 架构里的 Controller 就是一个中介者。Model 和 View 互不通信,都通过 Controller 协调。
Java 的 java.util.Timer 内部也是中介者模式的思路——TimerTask 不需要知道其他 Task 的存在,Timer 统一管理调度。
连前端的 Redux/Vuex 本质上也是中介者——组件不直接互相通信,而是通过全局 Store(中介者)来共享状态。
别滥用的忠告
中介者模式最大的陷阱是中介者本身变成一个上帝类。所有逻辑都塞进 Mediator,它膨胀到几千行,变成整个系统的瓶颈。这种情况其实比你原来的网状依赖还糟,至少原来问题分散在各个组件里,现在全集中到一个类了。
我们的做法是按业务域拆分中介者。价格相关的事件放 PriceMediator,航班状态相关放 FlightMediator,不要什么都往一个 Mediator 里塞。Mediator 应该是一个薄协调层,不应该包含具体的业务计算逻辑。
另一个常见问题:事件风暴。组件 A 触发事件 -> Mediator 通知组件 B -> B 改变了状态 -> 又触发事件 -> Mediator 通知 A -> A 又改变状态... 循环调用。解决办法是在 Mediator 里维护一个事件处理标记,同一轮事件中不再重复派发。
微信搜「爪爪代码冒险记」,漫画闯关学设计模式,23种模式全在里面。
