1. 中介者模式的核心思想与应用场景
中介者模式(Mediator Pattern)是行为型设计模式中的一种,它通过引入一个中介对象来封装一组对象之间的交互关系。想象一下机场的塔台控制系统——如果没有塔台调度员,每架飞机都需要直接与其他所有飞机通信来确定航线,这将导致通信链路呈指数级增长,系统复杂度急剧上升。中介者模式正是为了解决这类"多对多"交互场景而诞生的。
在Java开发中,当遇到以下情况时,中介者模式往往能发挥奇效:
- 聊天室系统(用户之间需要相互通信但不应直接耦合)
- MVC架构中的Controller层(协调View和Model的交互)
- 分布式系统的消息中间件(解耦消息生产者和消费者)
- GUI组件的事件处理(按钮、输入框等组件的联动)
我曾在电商平台的订单系统中应用过中介者模式。最初的设计中,订单服务、库存服务、支付服务和物流服务直接相互调用,形成了复杂的网状依赖。当需要新增一个优惠券服务时,不得不修改四个现有服务的代码。引入订单中介者后,所有服务只与中介者交互,新增功能只需扩展中介者逻辑,各服务保持稳定。
2. 中介者模式的UML结构与Java实现
2.1 标准UML类图解析
中介者模式包含四个核心角色:
code复制┌─────────────┐ ┌─────────────┐
│ Mediator │<>---->│ Colleague │
└─────────────┘ └─────────────┘
^ ^
| |
┌─────────────┐ ┌─────────────┐
│ConcreteMediator│ │ConcreteColleague│
└─────────────┘ └─────────────┘
具体到Java代码实现,我们以聊天室为例:
java复制// 抽象中介者
public interface ChatMediator {
void sendMessage(String msg, User user);
void addUser(User user);
}
// 具体中介者
public class ChatMediatorImpl implements ChatMediator {
private List<User> users;
public ChatMediatorImpl() {
this.users = new ArrayList<>();
}
@Override
public void sendMessage(String msg, User user) {
for(User u : users) {
// 不发送给消息发起者
if(u != user) {
u.receive(msg);
}
}
}
@Override
public void addUser(User user) {
users.add(user);
}
}
// 抽象同事类
public abstract class User {
protected ChatMediator mediator;
protected String name;
public User(ChatMediator med, String name) {
this.mediator = med;
this.name = name;
}
public abstract void send(String msg);
public abstract void receive(String msg);
}
// 具体同事类
public class UserImpl extends User {
public UserImpl(ChatMediator med, String name) {
super(med, name);
}
@Override
public void send(String msg) {
System.out.println(name + " 发送消息: " + msg);
mediator.sendMessage(msg, this);
}
@Override
public void receive(String msg) {
System.out.println(name + " 收到消息: " + msg);
}
}
2.2 模式的关键实现技巧
- 中介者持有同事引用:在
ChatMediatorImpl中维护了List<User>,这是星型结构的核心 - 同事类仅依赖中介者接口:
User类只知道ChatMediator接口,不与具体实现耦合 - 双向通信机制:同事→中介者通过方法调用,中介者→同事通过回调接口
实际开发中,我推荐使用泛型来增强类型安全:
java复制public interface Mediator<T extends Colleague> {
void mediate(T sender, String event);
}
public abstract class Colleague<T extends Mediator<?>> {
protected final T mediator;
protected Colleague(T mediator) {
this.mediator = mediator;
}
}
3. 中介者模式在复杂业务中的实战应用
3.1 电商订单系统的改造案例
假设我们有一个初始的订单处理流程:
java复制class OrderService {
private InventoryService inventory;
private PaymentService payment;
private LogisticsService logistics;
public void placeOrder(Order order) {
inventory.checkStock(order);
payment.processPayment(order);
logistics.arrangeDelivery(order);
// 新增需求:需要添加优惠券核销
}
}
引入中介者后的改进方案:
java复制interface OrderMediator {
void placeOrder(Order order);
void addService(OrderService service);
}
class OrderMediatorImpl implements OrderMediator {
private List<OrderService> services = new ArrayList<>();
@Override
public void placeOrder(Order order) {
for(OrderService service : services) {
service.process(order);
}
}
@Override
public void addService(OrderService service) {
services.add(service);
}
}
abstract class OrderService {
protected OrderMediator mediator;
protected OrderService(OrderMediator mediator) {
this.mediator = mediator;
mediator.addService(this);
}
abstract void process(Order order);
}
3.2 性能优化与线程安全考量
在高并发场景下,中介者可能成为性能瓶颈。我在实际项目中遇到过这些优化点:
- 异步中介者:使用消息队列实现异步处理
java复制class AsyncOrderMediator implements OrderMediator {
private Executor executor = Executors.newFixedThreadPool(4);
private BlockingQueue<OrderEvent> queue = new LinkedBlockingQueue<>();
public AsyncOrderMediator() {
new Thread(this::processQueue).start();
}
private void processQueue() {
while(true) {
try {
OrderEvent event = queue.take();
executor.execute(() ->
event.getService().process(event.getOrder()));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
@Override
public void placeOrder(Order order) {
for(OrderService service : services) {
queue.put(new OrderEvent(service, order));
}
}
}
- 中介者缓存:对于读多写少的场景,可以引入缓存机制
java复制class CachedMediator implements ChatMediator {
private Map<String, List<User>> topicSubscribers = new ConcurrentHashMap<>();
@Override
public void sendMessage(String topic, String msg, User sender) {
List<User> subscribers = topicSubscribers.getOrDefault(topic,
Collections.emptyList());
subscribers.forEach(u -> u.receive(msg));
}
}
4. 中介者模式的进阶应用与反模式
4.1 与观察者模式的组合应用
中介者模式常与观察者模式结合使用,形成更灵活的事件驱动架构:
java复制class EventMediator implements ChatMediator {
private Map<EventType, List<Consumer<Event>>> handlers = new EnumMap<>(EventType.class);
public void registerHandler(EventType type, Consumer<Event> handler) {
handlers.computeIfAbsent(type, k -> new ArrayList<>()).add(handler);
}
@Override
public void sendMessage(Event event) {
handlers.getOrDefault(event.getType(), Collections.emptyList())
.forEach(h -> h.accept(event));
}
}
enum EventType { MESSAGE, NOTIFICATION, SYSTEM_ALERT }
4.2 常见反模式与规避方法
-
上帝对象反模式:中介者承担过多职责
- 解决方法:按业务领域拆分多个中介者
- 示例:将
OrderMediator拆分为PaymentMediator、InventoryMediator等
-
过度中介化:简单交互也引入中介者
- 判断标准:对象间交互是否真的复杂到需要解耦
- 经验法则:当对象引用关系超过5个时考虑引入中介者
-
循环依赖陷阱:中介者与同事相互持有强引用
- 解决方案:使用弱引用或事件总线
java复制class WeakRefMediator { private List<WeakReference<Colleague>> colleagues = new ArrayList<>(); public void addColleague(Colleague c) { colleagues.add(new WeakReference<>(c)); } }
我在重构一个遗留系统时,曾遇到一个包含2000行代码的"超级中介者"。通过领域分析,我们将其拆分为12个专门的中介者,每个负责特定的业务流,使系统可维护性大幅提升。
5. 中介者模式在框架中的典型应用
5.1 Spring框架中的中介者实现
Spring的事件机制是中介者模式的经典实现:
java复制// 定义事件
class OrderEvent extends ApplicationEvent {
public OrderEvent(Order source) {
super(source);
}
}
// 事件发布者(同事)
@Service
class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
public void placeOrder(Order order) {
publisher.publishEvent(new OrderEvent(order));
}
}
// 事件监听者(同事)
@Component
class InventoryListener {
@EventListener
public void handleOrder(OrderEvent event) {
// 处理库存逻辑
}
}
5.2 JMS消息中间件中的模式应用
ActiveMQ等消息队列实现了分布式中介者:
java复制// 生产者
class OrderProducer {
private ConnectionFactory factory;
public void sendOrder(Order order) {
try (Connection conn = factory.createConnection()) {
Session session = conn.createSession(false, AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(queue);
ObjectMessage msg = session.createObjectMessage(order);
producer.send(msg);
}
}
}
// 消费者
class InventoryConsumer implements MessageListener {
@Override
public void onMessage(Message message) {
Order order = (Order)((ObjectMessage)message).getObject();
// 处理订单
}
}
在微服务架构中,我经常使用这种模式来实现服务间的解耦。比如订单服务完成支付后,通过消息队列通知库存服务扣减库存,而两个服务完全不知道对方的存在。
6. 中介者模式的测试策略与调试技巧
6.1 单元测试模式
对中介者模式进行测试时,需要特别注意:
- 测试同事类时,使用Mock中介者
- 测试中介者时,使用Mock同事对象
- 验证交互的完整链路
使用Mockito的测试示例:
java复制@Test
public void testUserSendsMessage() {
ChatMediator mediator = mock(ChatMediator.class);
User user = new UserImpl(mediator, "TestUser");
user.send("Hello");
verify(mediator).sendMessage(eq("Hello"), eq(user));
}
@Test
public void testMediatorBroadcastsMessage() {
User user1 = mock(User.class);
User user2 = mock(User.class);
ChatMediator mediator = new ChatMediatorImpl();
mediator.addUser(user1);
mediator.addUser(user2);
mediator.sendMessage("Test", mock(User.class));
verify(user1).receive("Test");
verify(user2).receive("Test");
}
6.2 调试复杂交互的技巧
当中介者系统出现问题时,我常用的诊断方法:
- 添加日志标记:为每个消息分配唯一ID
java复制class TraceableMediator implements ChatMediator {
private AtomicLong counter = new AtomicLong();
@Override
public void sendMessage(String msg, User user) {
long traceId = counter.incrementAndGet();
log.info("[{}] {} sends: {}", traceId, user.getName(), msg);
// ...转发逻辑
log.info("[{}] message delivered", traceId);
}
}
- 可视化交互流程:使用PlantUML生成序列图
plantuml复制@startuml
participant UserA
participant Mediator
participant UserB
UserA -> Mediator : send("Hi")
Mediator -> UserB : receive("Hi")
@enduml
- 断点策略:在以下位置设置条件断点
- 中介者的消息接收点
- 同事类的消息发送点
- 中介者的消息转发决策点
7. 中介者模式的变体与扩展
7.1 链式中介者
对于需要多级处理的场景,可以实现中介者链:
java复制interface ChainableMediator extends ChatMediator {
void setNext(ChainableMediator next);
}
class ProfanityFilter implements ChainableMediator {
private ChainableMediator next;
private List<String> bannedWords = Arrays.asList("bad", "word");
@Override
public void setNext(ChainableMediator next) {
this.next = next;
}
@Override
public void sendMessage(String msg, User user) {
String filtered = bannedWords.stream()
.reduce(msg, (text, word) -> text.replaceAll(word, "***"));
if(next != null) {
next.sendMessage(filtered, user);
}
}
}
7.2 分布式中介者
在微服务架构下,可以使用Redis实现跨服务中介:
java复制class RedisMediator implements ChatMediator {
private RedisTemplate<String, String> redis;
private String channel;
public RedisMediator(RedisTemplate<String, String> redis, String channel) {
this.redis = redis;
this.channel = channel;
redis.listen(channel, (msg, user) -> notifySubscribers(msg));
}
@Override
public void sendMessage(String msg, User user) {
redis.send(channel, new Message(msg, user));
}
}
7.3 中介者与CQRS模式结合
对于读写分离架构,中介者可以很好地将命令与查询分离:
java复制class CQRSMediator {
private CommandBus commandBus;
private QueryBus queryBus;
public <R> R execute(Command<R> command) {
return commandBus.execute(command);
}
public <R> R query(Query<R> query) {
return queryBus.execute(query);
}
}
在最近的一个物联网项目中,我们使用这种架构处理设备命令和状态查询,实现了每秒处理10万+消息的高吞吐量系统。
8. 中介者模式的最佳实践与经验总结
经过多个项目的实践,我总结了以下中介者模式的使用心得:
- 适用性判断矩阵
| 条件 | 适合使用中介者 | 不适合使用中介者 |
|---|---|---|
| 对象交互复杂度 | 高(网状结构) | 低(星型或简单链式) |
| 交互变化频率 | 高(经常新增交互) | 低(交互关系稳定) |
| 系统扩展需求 | 多(预期频繁扩展) | 少(功能基本固定) |
| 对象复用需求 | 强(需要独立复用) | 弱(总是整体使用) |
- 性能优化检查表
- [ ] 中介者是否成为单点瓶颈?
- [ ] 是否可以引入异步处理?
- [ ] 消息传递是否需要批量处理?
- [ ] 是否可以使用弱引用减少内存占用?
- [ ] 是否需要引入缓存机制?
- 代码质量指标
- 中介者类的圈复杂度应控制在10以下
- 同事类不应包含业务逻辑,只应包含与中介者的交互逻辑
- 中介者接口的方法数建议不超过7个(米勒定律)
- 团队协作建议
- 为中介者编写详细的协议文档
- 使用契约测试保证同事类与中介者的兼容性
- 在团队内部分享中介者的典型使用模式
我在实际项目中最有价值的经验是:中介者模式不是银弹,它最适合解决特定的复杂交互问题。当系统刚开始演化时,过早引入中介者可能会增加不必要的复杂度。最佳引入时机是当你第三次为对象间交互关系修改代码时——这时系统已经显示出明确的复杂交互特征,中介者的收益将超过其成本。
