本文是【GoF设计模式】系列第15篇,更多内容欢迎关注公众号:咖啡八杯
前言
为什么需要中介者模式?
假设在设计一个智能家居系统,家里有空调、窗帘、灯三种设备。业务规则是:空调开启时自动关闭窗帘(避免冷气流失),窗帘关闭时自动开灯(补充采光)。
最直觉的写法是让设备之间直接互相调用:
java
class AirConditioner {
private Curtain curtain;
private Light light;
public void turnOn() {
// 空调开启逻辑
curtain.close(); // 直接调用窗帘
}
}
class Curtain {
private Light light;
public void close() {
// 窗帘关闭逻辑
light.turnOn(); // 直接调用灯
}
}
这种写法的问题很快暴露:空调需要知道窗帘,窗帘需要知道灯,设备之间形成复杂的网状依赖。如果新增一个设备(如加湿器),需要修改所有相关设备的代码。更麻烦的是,如果业务规则变了(比如空调开启时也要开灯),就得改空调的代码,违反开闭原则。
中介者模式解决的就是这种"多个对象之间存在复杂联动关系"的耦合问题。把所有联动规则集中到一个中介者对象里,设备只跟中介者通信,互不认识。
概念
中介者模式(Mediator Pattern)是一种行为型设计模式 ,核心思想是用一个中介对象封装一系列对象之间的交互,使各对象不需要显式地互相引用,从而使其耦合松散,而且可以独立改变它们之间的交互。
可以把它想象成航空管制塔台:每架飞机(同事对象)不需要知道其他飞机的位置和航线,只需要向塔台(中介者)报告自己的状态,塔台根据全局信息协调所有飞机的起降和飞行路径。飞机之间互不通信,全部通过塔台协调。
中介者模式涉及四个角色:
- Mediator(抽象中介者):定义中介者接口,用于各同事对象之间的通信
- ConcreteMediator(具体中介者):实现中介者接口,负责协调各同事对象的交互关系,需要知道所有同事类
- Colleague(抽象同事类):定义同事类接口,维护一个对中介者对象的引用
- ConcreteColleague(具体同事类):实现同事类接口,每个同事类只知道自己行为,通过中介者与其他同事交互
classDiagram direction BT class Mediator { <<interface>> +register(colleague: Colleague) +send(message: String, colleague: Colleague) } class ConcreteMediator { -colleagues: List~Colleague~ +register(colleague: Colleague) +send(message: String, colleague: Colleague) } class Colleague { <<abstract>> #mediator: Mediator +send(message: String) +receive(message: String) } class ConcreteColleagueA { +send(message: String) +receive(message: String) } class ConcreteColleagueB { +send(message: String) +receive(message: String) } ConcreteMediator ..|> Mediator : 实现 ConcreteColleagueA --|> Colleague : 继承 ConcreteColleagueB --|> Colleague : 继承 Mediator o--> Colleague : 持有同事列表 Colleague --> Mediator : 持有中介者引用
图中各类之间的关系:Mediator 接口定义了 register 和 send 方法,ConcreteMediator 实现该接口并持有 List<Colleague>,Colleague 抽象类持有 Mediator 引用并通过它发送消息------同事类之间不直接通信,全部通过中介者协调。
实现
GoF 的标准实现中,中介者维护所有同事的引用,同事通过中介者转发消息。当同事状态变化时,通知中介者,中介者根据业务规则决定通知哪些其他同事。
标准实现
定义一个 Mediator 接口作为抽象中介者,声明 register(注册同事)和 send(转发消息)方法。Colleague 作为抽象同事类,持有中介者引用,提供 send(发送消息)和 receive(接收消息)方法。具体中介者 ConcreteMediator 维护同事列表,在 send 方法中实现消息转发策略。具体同事类 ConcreteColleagueA 和 ConcreteColleagueB 通过中介者与其他同事交互。
java
// 抽象中介者
interface Mediator {
public void register(Colleague colleague);
public void send(String message, Colleague colleague);
}
// 抽象同事类
abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public abstract void receive(String message);
public abstract void send(String message);
}
// 具体中介者
class ConcreteMediator implements Mediator {
private List<Colleague> colleagues = new ArrayList<>();
public void register(Colleague colleague) {
colleagues.add(colleague);
}
public void send(String message, Colleague colleague) {
// 转发给除发送者外的所有同事
for (Colleague c : colleagues) {
if (c != colleague) {
c.receive(message);
}
}
}
}
// 具体同事类A
class ConcreteColleagueA extends Colleague {
public ConcreteColleagueA(Mediator mediator) {
super(mediator);
}
public void receive(String message) {
System.out.println("ColleagueA 收到消息: " + message);
}
public void send(String message) {
System.out.println("ColleagueA 发送消息: " + message);
mediator.send(message, this);
}
}
// 具体同事类B
class ConcreteColleagueB extends Colleague {
public ConcreteColleagueB(Mediator mediator) {
super(mediator);
}
public void receive(String message) {
System.out.println("ColleagueB 收到消息: " + message);
}
public void send(String message) {
System.out.println("ColleagueB 发送消息: " + message);
mediator.send(message, this);
}
}
角色对照:
- Mediator(抽象中介者) :
Mediator接口,定义register和send方法 - ConcreteMediator(具体中介者) :
ConcreteMediator,维护同事列表,实现消息转发策略 - Colleague(抽象同事类) :
Colleague抽象类,持有中介者引用,提供send和receive方法 - ConcreteColleague(具体同事类) :
ConcreteColleagueA、ConcreteColleagueB,通过中介者与其他同事交互
关键点:同事类只负责自身状态,通过中介者转发消息;中介者维护所有同事引用,根据业务规则决定消息转发策略。同事之间不直接通信,全部通过中介者协调。
引入一个具体场景:智能家居系统,空调、窗帘、灯三种设备通过中控(中介者)协调联动。空调开启时自动关闭窗帘(避免冷气流失),窗帘关闭时自动开灯(补充采光)。
java
// 抽象中介者
interface SmartHomeMediator {
public void register(Device device);
public void notify(String event, Device device);
}
// 抽象同事类:设备基类
abstract class Device {
protected SmartHomeMediator mediator;
protected String name;
protected boolean state;
public Device(SmartHomeMediator mediator, String name) {
this.mediator = mediator;
this.name = name;
this.state = false;
}
public String getName() { return name; }
public boolean getState() { return state; }
public void setState(boolean state) {
this.state = state;
System.out.println(name + " " + (state ? "开启" : "关闭"));
mediator.notify(state ? "on" : "off", this);
}
public abstract void handleEvent(String event);
}
// 具体中介者:智能家居中控
class ConcreteSmartHomeMediator implements SmartHomeMediator {
private List<Device> devices = new ArrayList<>();
public void register(Device device) {
devices.add(device);
}
public void notify(String event, Device device) {
// 空调开启 → 关闭窗帘
if (device instanceof AirConditioner && event.equals("on")) {
for (Device d : devices) {
if (d instanceof Curtain) {
d.setState(false);
}
}
}
// 窗帘关闭 → 开启灯
if (device instanceof Curtain && event.equals("off")) {
for (Device d : devices) {
if (d instanceof Light) {
d.setState(true);
}
}
}
}
}
// 具体同事类:空调
class AirConditioner extends Device {
public AirConditioner(SmartHomeMediator mediator) {
super(mediator, "空调");
}
public void handleEvent(String event) {
// 空调不处理其他设备的事件
}
}
// 具体同事类:窗帘
class Curtain extends Device {
public Curtain(SmartHomeMediator mediator) {
super(mediator, "窗帘");
}
public void handleEvent(String event) {
// 窗帘不处理其他设备的事件
}
}
// 具体同事类:灯
class Light extends Device {
public Light(SmartHomeMediator mediator) {
super(mediator, "灯");
}
public void handleEvent(String event) {
// 灯不处理其他设备的事件
}
}
角色对照:
- Mediator(抽象中介者) :
SmartHomeMediator接口,定义register和notify方法 - ConcreteMediator(具体中介者) :
ConcreteSmartHomeMediator,维护设备列表,实现联动规则 - Colleague(抽象同事类) :
Device抽象类,持有中介者引用,状态变化时通知中介者 - ConcreteColleague(具体同事类) :
AirConditioner、Curtain、Light,具体设备实现
关键点 :设备状态变化时调用 setState,在 setState 中通知中介者;中介者根据事件类型和设备类型执行联动规则;如果业务规则变化(比如空调开启时也要开灯),只需修改中介者的 notify 方法,设备类不变。
总结
本质:用一个中介对象封装多个对象之间的交互,将网状依赖转化为星形结构。
什么时候用:
- 多个对象之间存在复杂的网状依赖关系
- 需要集中管理对象间的交互规则
- 想要降低对象间的耦合度,提高可维护性
什么时候不用:
- 对象之间只有简单的两两交互,引入中介者反而增加复杂度
- 性能要求极高,中介者转发消息的开销不可接受
- 中介者本身会变得过于庞大复杂(上帝对象)
简单记忆:中介者管协调,同事只管报;规则集中改,耦合自然少。
相似模式区分
总览
| 模式 | 核心意图 | 典型场景 |
|---|---|---|
| 中介者 | 协调多个对象间的复杂交互 | 智能家居中控、聊天室、GUI组件联动 |
| 观察者 | 一对多的通知机制 | 事件监听、消息订阅、数据绑定 |
| 门面 | 为子系统提供统一入口 | API封装、SDK入口、简化调用 |
中介者 vs 观察者
| 维度 | 中介者模式 | 观察者模式 |
|---|---|---|
| 核心意图 | 集中协调多对象间的双向交互 | 一对多的单向通知 |
| 结构差异 | 中介者知道所有同事,同事只知道中介者 | 主题知道所有观察者,观察者只知道主题 |
| 关注点 | 对象间的交互规则和联动逻辑 | 状态变化的通知分发 |
| 典型场景 | 智能家居中控、聊天室、工作流引擎 | 事件监听、消息订阅、数据绑定 |
逐步区分法:
- 如果对象间需要双向交互(A变化影响B,B变化可能再影响A)→ 选中介者
- 如果只是单向通知(主题变化通知观察者,观察者不反向影响主题)→ 选观察者
- 如果联动规则复杂需要集中管理 → 选中介者
简单记忆口诀:中介者管双向协调,观察者管单向通知。
推荐:大多数场景下,复杂联动用中介者,简单通知用观察者。Spring事件机制是观察者的变体,但可以用于简单联动场景。
中介者 vs 门面
| 维度 | 中介者模式 | 门面模式 |
|---|---|---|
| 核心意图 | 协调多对象间的交互,降低耦合 | 为复杂子系统提供统一入口,简化调用 |
| 结构差异 | 中介者参与业务逻辑,协调对象间的交互 | 门面只是封装调用入口,不参与子系统内部逻辑 |
| 关注点 | 对象间的交互规则 | 子系统的使用接口 |
| 典型场景 | 聊天室、GUI组件联动、工作流引擎 | API封装、SDK入口、简化配置 |
逐步区分法:
- 如果需要协调多个对象间的交互(对象间有联动)→ 选中介者
- 如果只是简化调用入口(提供一个统一接口调用多个子系统)→ 选门面
- 如果对象需要反向通知中介者 → 选中介者(门面是单向的)
简单记忆口诀:中介者管交互,门面管入口。
推荐:门面用于简化外部调用,中介者用于协调内部交互。两者可以组合使用:门面作为外部入口,中介者协调内部逻辑。
练习题目
智能家居中控系统
题目描述:小明正在设计一个智能家居系统,系统中有灯(Light)、窗帘(Curtain)、空调(AC)三种设备,它们通过智能家居中控(中介者)来协调联动。设备之间不直接通信,所有联动逻辑由中控负责。
中控的联动规则:
- 空调开启 → 自动关闭窗帘(避免冷气流失)
- 空调关闭 → 自动打开窗帘
- 窗帘关闭 → 自动开灯(补充采光)
- 窗帘打开 → 自动关灯(自然光足够)
注意:联动可能产生连锁反应。例如开启空调 → 关闭窗帘 → 开灯。同一设备在同一次连锁中状态只改变一次,避免循环触发。
输入描述:第一行包含三个字符串,分别是灯、窗帘、空调的名称,用空格分隔。第二行包含三个字符串,分别是灯、窗帘、空调的初始状态(on 或 off),用空格分隔。接下来每行包含一个操作,格式为设备名 操作(操作为 on 或 off),直到输入结束。
输出描述:对于每个操作,按联动触发顺序输出每个状态发生变化的设备,格式为设备名 on 或设备名 off。若操作未导致状态变化(设备已在目标状态),则不输出。
输入示例:
Light Curtain AC
off on off
AC on
AC off
Curtain off
输出示例:
AC on
Curtain off
Light on
AC off
Curtain on
Light off
Curtain off
Light on
解题思路:本题的核心是中介者模式的连锁反应机制。
角色对应:
- Mediator(抽象中介者) :
Mediator接口,定义send方法 - ConcreteMediator(具体中介者) :
SmartHomeMediator,实现联动规则(空调→窗帘→灯) - Colleague(抽象同事类) :
Device抽象类,持有中介者引用,提供send和receive方法 - ConcreteColleague(具体同事类) :
Light、Curtain、AC,具体设备实现
连锁反应机制:
- 用户操作设备(如开启空调)→ 调用
device.send("on") send方法更新状态并输出,然后通知中介者mediator.send("on", this)- 中介者根据联动规则,调用相关设备的
receive方法(如关闭窗帘) receive方法更新状态并输出,再次通知中介者,形成递归连锁- 避免循环触发:在
send和receive中检查if (!status.equals(message)),如果状态已经是目标状态则不处理
java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
String[] names = line.split(" ");
line = sc.nextLine();
String[] status = line.split(" ");
Mediator mediator = new SmartHomeMediator();
Light light = new Light(mediator, names[0], status[0]);
Curtain curtain = new Curtain(mediator, names[1], status[1]);
AC ac = new AC(mediator, names[2], status[2]);
mediator.setLight(light);
mediator.setCurtain(curtain);
mediator.setAc(ac);
Map<String, Device> map = new HashMap<>();
map.put(names[0], light);
map.put(names[1], curtain);
map.put(names[2], ac);
while (sc.hasNext()) {
String name = sc.next();
String s = sc.next();
Device d = map.get(name);
d.send(s);
}
}
}
// 抽象中介者
interface Mediator {
public void send(String message, Device device);
}
// 具体中介者:智能家居中控
class SmartHomeMediator implements Mediator {
private Light light;
private Curtain curtain;
private AC ac;
public void setLight(Light light) { this.light = light; }
public void setCurtain(Curtain curtain) { this.curtain = curtain; }
public void setAc(AC ac) { this.ac = ac; }
public void send(String message, Device device) {
// 空调状态变化触发窗帘联动
if (device instanceof AC) {
if (message.equals("on")) {
curtain.receive("off"); // 规则1:空调开 → 关窗帘
} else {
curtain.receive("on"); // 规则2:空调关 → 开窗帘
}
}
// 窗帘状态变化触发灯联动
else if (device instanceof Curtain) {
if (message.equals("on")) {
light.receive("off"); // 规则4:窗帘开 → 关灯
} else {
light.receive("on"); // 规则3:窗帘关 → 开灯
}
}
// 灯状态变化不触发联动
}
}
// 抽象同事类:设备基类
abstract class Device {
protected Mediator mediator;
protected String name;
protected String status;
public Device(Mediator mediator, String name, String status) {
this.mediator = mediator;
this.name = name;
this.status = status;
}
public String getName() { return name; }
public String getStatus() { return status; }
// 用户操作触发状态变化
public void send(String message) {
if (!status.equals(message)) {
System.out.println(name + " " + message);
status = message;
mediator.send(message, this); // 通知中介者
}
}
// 中介者通知触发状态变化
public void receive(String message) {
if (!status.equals(message)) {
System.out.println(name + " " + message);
status = message;
mediator.send(message, this); // 状态变化后通知中介者,形成连锁
}
}
}
// 具体同事类:灯
class Light extends Device {
public Light(Mediator mediator, String name, String status) {
super(mediator, name, status);
}
}
// 具体同事类:窗帘
class Curtain extends Device {
public Curtain(Mediator mediator, String name, String status) {
super(mediator, name, status);
}
}
// 具体同事类:空调
class AC extends Device {
public AC(Mediator mediator, String name, String status) {
super(mediator, name, status);
}
}
扩展:实际项目中的中介者模式
Spring事件机制(ApplicationEventPublisher)
Spring的事件机制本质上是中介者模式的变体:ApplicationEventPublisher 作为中介者,事件监听器作为同事类,通过事件机制解耦组件间的交互。
场景:订单系统中,订单创建后需要通知库存系统扣减库存、通知支付系统生成支付单、通知消息系统发送通知。如果订单直接依赖库存、支付、消息系统,耦合度极高。
角色对照:
- Mediator :
ApplicationEventPublisher(Spring容器提供) - Colleague :各个事件监听器(
@EventListener标注的方法) - 事件对象:同事之间传递的消息载体
java
// 事件对象:订单创建事件
public class OrderCreatedEvent extends ApplicationEvent {
private String orderId;
private BigDecimal amount;
public OrderCreatedEvent(Object source, String orderId, BigDecimal amount) {
super(source);
this.orderId = orderId;
this.amount = amount;
}
public String getOrderId() { return orderId; }
public BigDecimal getAmount() { return amount; }
}
// 同事类A:库存服务监听器
@Service
public class InventoryEventListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("扣减库存,订单号:" + event.getOrderId());
// 调用库存服务扣减库存
}
}
// 同事类B:支付服务监听器
@Service
public class PaymentEventListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("生成支付单,金额:" + event.getAmount());
// 调用支付服务生成支付单
}
}
// 同事类C:消息服务监听器
@Service
public class MessageEventListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("发送订单创建通知,订单号:" + event.getOrderId());
// 调用消息服务发送通知
}
}
// 订单服务:发布事件(不依赖任何监听器)
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher; // 注入中介者
public void createOrder(String orderId, BigDecimal amount) {
// 创建订单逻辑...
System.out.println("订单创建成功:" + orderId);
// 发布事件,通知所有监听器(不需要知道有哪些监听器)
publisher.publishEvent(new OrderCreatedEvent(this, orderId, amount));
}
}
关键点 :ApplicationEventPublisher 作为中介者,发布事件后自动通知所有监听器;监听器通过 @EventListener 注解声明自己关心的消息类型;订单服务只需要发布事件,不需要知道有哪些监听器,耦合度降到最低。
GUI对话框组件协调
GUI界面中多个控件(按钮、文本框、下拉框)之间的联动逻辑复杂时,使用中介者集中管理交互规则。
场景:用户注册对话框包含用户名输入框、密码输入框、确认密码框、注册按钮。联动规则:用户名输入后检查是否已存在,密码和确认密码不一致时提示错误,所有验证通过后注册按钮才可用。
角色对照:
- Mediator :
DialogMediator接口 - ConcreteMediator :
RegisterDialogMediator,实现具体联动规则 - Colleague :
UIComponent抽象类,所有UI组件的基类 - ConcreteColleague :
TextBox、Button等具体组件
java
// 抽象中介者
interface DialogMediator {
public void notify(UIComponent component, String event);
}
// 抽象同事类:UI组件基类
abstract class UIComponent {
protected DialogMediator mediator;
protected String name;
public UIComponent(DialogMediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public String getName() { return name; }
// 组件状态变化时通知中介者
public void changed() {
mediator.notify(this, "changed");
}
public abstract void setEnabled(boolean enabled);
public abstract String getValue();
}
// 具体同事类:文本框
class TextBox extends UIComponent {
private String value = "";
public TextBox(DialogMediator mediator, String name) {
super(mediator, name);
}
public void setValue(String value) {
this.value = value;
changed(); // 值变化时通知中介者
}
public String getValue() { return value; }
public void setEnabled(boolean enabled) {
System.out.println(name + (enabled ? " 启用" : " 禁用"));
}
}
// 具体同事类:按钮
class Button extends UIComponent {
private boolean enabled = false;
public Button(DialogMediator mediator, String name) {
super(mediator, name);
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
System.out.println(name + (enabled ? " 启用" : " 禁用"));
}
public String getValue() { return String.valueOf(enabled); }
public void click() {
if (enabled) {
System.out.println(name + " 被点击");
mediator.notify(this, "click");
}
}
}
// 具体中介者:注册对话框
class RegisterDialogMediator implements DialogMediator {
private TextBox usernameBox;
private TextBox passwordBox;
private TextBox confirmBox;
private Button registerButton;
private Label messageLabel;
public RegisterDialogMediator(TextBox usernameBox, TextBox passwordBox,
TextBox confirmBox, Button registerButton,
Label messageLabel) {
this.usernameBox = usernameBox;
this.passwordBox = passwordBox;
this.confirmBox = confirmBox;
this.registerButton = registerButton;
this.messageLabel = messageLabel;
}
public void notify(UIComponent component, String event) {
// 用户名变化 → 检查是否已存在
if (component == usernameBox && event.equals("changed")) {
String username = usernameBox.getValue();
if (isUsernameExists(username)) {
messageLabel.setText("用户名已存在");
registerButton.setEnabled(false);
} else {
messageLabel.setText("");
validateForm();
}
}
// 密码或确认密码变化 → 检查是否一致
else if ((component == passwordBox || component == confirmBox)
&& event.equals("changed")) {
String password = passwordBox.getValue();
String confirm = confirmBox.getValue();
if (!password.isEmpty() && !confirm.isEmpty() && !password.equals(confirm)) {
messageLabel.setText("两次密码不一致");
registerButton.setEnabled(false);
} else {
messageLabel.setText("");
validateForm();
}
}
// 注册按钮点击 → 执行注册
else if (component == registerButton && event.equals("click")) {
doRegister();
}
}
private void validateForm() {
boolean valid = !usernameBox.getValue().isEmpty()
&& !passwordBox.getValue().isEmpty()
&& passwordBox.getValue().equals(confirmBox.getValue())
&& !isUsernameExists(usernameBox.getValue());
registerButton.setEnabled(valid);
}
private boolean isUsernameExists(String username) {
// 模拟检查用户名是否存在
return "admin".equals(username);
}
private void doRegister() {
System.out.println("注册成功:" + usernameBox.getValue());
}
}
关键点 :每个组件只知道自己状态,通过 changed() 通知中介者;中介者根据变化类型执行不同的联动规则;如果联动规则变化(如增加邮箱验证),只需修改中介者,组件类不变。
MVC架构中的Controller
MVC架构中,Controller作为中介者协调Model和View的交互,避免Model和View直接耦合。
场景:用户列表页面,View显示用户列表,Model提供用户数据。用户点击删除按钮时,View通知Controller,Controller调用Model删除用户,Model返回结果后Controller更新View。
角色对照:
- Mediator :
UserController - Colleague :
UserModel(数据层)、UserView(视图层)
java
// Model:用户数据(同事类A)
class UserModel {
private List<String> users = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
public List<String> getUsers() { return new ArrayList<>(users); }
public boolean deleteUser(String username) {
return users.remove(username);
}
}
// View:用户视图(同事类B)
class UserView {
public void showUsers(List<String> users) {
System.out.println("用户列表:" + users);
}
public void showMessage(String message) {
System.out.println("提示:" + message);
}
// 用户点击删除按钮,通知Controller
public void onDeleteClicked(String username, UserController controller) {
controller.handleDelete(username);
}
}
// Controller:中介者
class UserController {
private UserModel model;
private UserView view;
public UserController(UserModel model, UserView view) {
this.model = model;
this.view = view;
}
// 协调Model和View的交互
public void handleDelete(String username) {
boolean success = model.deleteUser(username); // 调用Model
if (success) {
view.showMessage("删除成功:" + username); // 更新View
view.showUsers(model.getUsers()); // 刷新列表
} else {
view.showMessage("删除失败:" + username + " 不存在");
}
}
public void showUserList() {
view.showUsers(model.getUsers());
}
}
关键点:Model只负责数据操作,不关心View如何显示;View只负责显示和接收用户操作,不关心Model如何处理数据;Controller作为中介者协调两者,Model和View通过Controller通信。
多人聊天室
聊天室是中介者模式的经典应用:聊天室服务器作为中介者,用户作为同事类,用户之间不直接通信,都通过聊天室转发消息。
场景:多人聊天室,用户A发送消息时,聊天室服务器转发给所有其他用户。用户加入或退出时,聊天室通知所有用户。
角色对照:
- Mediator :
ChatRoom接口 - ConcreteMediator :
ConcreteChatRoom,维护用户列表,转发消息 - Colleague :
User抽象类 - ConcreteColleague :
ChatUser,具体用户实现
java
// 抽象中介者
interface ChatRoom {
public void register(User user);
public void send(String message, User sender);
}
// 抽象同事类:用户基类
abstract class User {
protected ChatRoom chatRoom;
protected String name;
public User(ChatRoom chatRoom, String name) {
this.chatRoom = chatRoom;
this.name = name;
}
public String getName() { return name; }
public abstract void receive(String message, String senderName);
public abstract void send(String message);
}
// 具体中介者:聊天室
class ConcreteChatRoom implements ChatRoom {
private List<User> users = new ArrayList<>();
public void register(User user) {
users.add(user);
System.out.println(user.getName() + " 加入聊天室");
// 通知其他用户
for (User u : users) {
if (u != user) {
u.receive(user.getName() + " 加入了聊天室", "系统");
}
}
}
public void send(String message, User sender) {
// 转发给除发送者外的所有用户
for (User u : users) {
if (u != sender) {
u.receive(message, sender.getName());
}
}
}
}
// 具体同事类:聊天用户
class ChatUser extends User {
public ChatUser(ChatRoom chatRoom, String name) {
super(chatRoom, name);
}
public void receive(String message, String senderName) {
System.out.println("[" + name + " 收到] " + senderName + ":" + message);
}
public void send(String message) {
System.out.println("[" + name + " 发送] " + message);
chatRoom.send(message, this); // 通过中介者转发
}
}
// 使用示例
public class ChatRoomDemo {
public static void main(String[] args) {
ChatRoom room = new ConcreteChatRoom();
User alice = new ChatUser(room, "Alice");
User bob = new ChatUser(room, "Bob");
User charlie = new ChatUser(room, "Charlie");
room.register(alice); // Alice加入,无其他用户
room.register(bob); // Bob加入,通知Alice
room.register(charlie);// Charlie加入,通知Alice和Bob
alice.send("大家好!"); // Alice发消息,Bob和Charlie收到
bob.send("Hi Alice!"); // Bob发消息,Alice和Charlie收到
}
}
关键点 :用户之间不直接通信,都通过聊天室转发,降低耦合;聊天室维护用户列表,负责消息转发和用户加入/离开通知;如果需要增加私聊功能,只需修改中介者的 send 方法增加目标参数。
技术交流 & 更多原创内容,关注公众号:咖啡八杯