中介者模式:对象交互的协调者
在软件开发中,我们经常会遇到多个对象之间相互通信的场景。当对象间的交互关系变得复杂时,系统往往会面临耦合度高、可维护性差的问题。中介者模式正是为了解决这类问题而生的行为型设计模式。
什么是中介者模式?
中介者模式(Mediator Pattern)用一个中介对象来封装一系列对象之间的交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
核心思想
中介者模式的核心思想是:将对象间的复杂交互关系转移到中介者对象中。原本需要相互通信的对象现在只需要与中介者通信,由中介者来协调各个对象的行为。
为什么需要中介者模式?
问题场景
想象一个聊天室系统的设计,在没有中介者模式的情况下,每个用户对象都需要维护对其他所有用户对象的引用:
// 问题代码:紧耦合的聊天室实现
class User {
private String name;
private List<User> users;
public User(String name, List<User> users) {
this.name = name;
this.users = users;
}
public void sendMessage(String message) {
for (User user : users) {
if (user != this) {
user.receiveMessage(this.name + ": " + message);
}
}
}
public void receiveMessage(String message) {
System.out.println(name + " 收到消息: " + message);
}
}
这种设计的缺点很明显:
- 耦合度高:每个用户都需要知道其他所有用户
- 难以维护:添加或删除用户需要修改所有相关对象
- 扩展性差:新增功能(如消息过滤、权限控制)需要在每个用户中实现
解决方案
引入中介者模式后,系统结构变得更加清晰:
// 中介者接口
interface ChatMediator {
void sendMessage(String message, User user);
void addUser(User user);
}
// 具体中介者
class ChatRoom implements ChatMediator {
private List<User> users;
public ChatRoom() {
this.users = new ArrayList<>();
}
@Override
public void addUser(User user) {
this.users.add(user);
}
@Override
public void sendMessage(String message, User sender) {
for (User user : users) {
// 不向发送者自己发送消息
if (user != sender) {
user.receiveMessage(message);
}
}
}
}
// 用户类
abstract class User {
protected ChatMediator mediator;
protected String name;
public User(ChatMediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public abstract void send(String message);
public abstract void receiveMessage(String message);
}
// 具体用户类
class ChatUser extends User {
public ChatUser(ChatMediator mediator, String name) {
super(mediator, name);
}
@Override
public void send(String message) {
System.out.println(this.name + " 发送消息: " + message);
mediator.sendMessage(this.name + ": " + message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println(this.name + " 收到消息: " + message);
}
}
中介者模式的结构
中介者模式包含以下几个核心角色:
1. Mediator(中介者接口)
定义各个同事对象交互的接口
2. ConcreteMediator(具体中介者)
实现中介者接口,协调各个同事对象的行为
3. Colleague(同事类)
每个同事对象都知道它的中介者对象,通过中介者与其他同事对象通信
实际应用示例
案例一:飞机塔台调度系统
在航空领域,塔台作为中介者协调所有飞机的起降:
// 塔台中介者
interface ControlTower {
void requestLanding(Aircraft aircraft);
void notifyTakeoff(Aircraft aircraft);
boolean isRunwayAvailable();
}
class AirportControlTower implements ControlTower {
private boolean runwayAvailable = true;
private Queue<Aircraft> landingQueue = new LinkedList<>();
@Override
public void requestLanding(Aircraft aircraft) {
if (runwayAvailable) {
runwayAvailable = false;
System.out.println("塔台: " + aircraft.getName() + " 可以降落");
aircraft.performLanding();
} else {
landingQueue.add(aircraft);
System.out.println("塔台: " + aircraft.getName() + " 请等待降落");
}
}
@Override
public void notifyTakeoff(Aircraft aircraft) {
runwayAvailable = true;
System.out.println("塔台: " + aircraft.getName() + " 已起飞");
if (!landingQueue.isEmpty()) {
Aircraft nextAircraft = landingQueue.poll();
requestLanding(nextAircraft);
}
}
@Override
public boolean isRunwayAvailable() {
return runwayAvailable;
}
}
// 飞机类
abstract class Aircraft {
protected ControlTower controlTower;
protected String name;
public Aircraft(ControlTower controlTower, String name) {
this.controlTower = controlTower;
this.name = name;
}
public String getName() {
return name;
}
public abstract void requestLanding();
public abstract void performLanding();
public abstract void takeoff();
}
class CommercialAircraft extends Aircraft {
public CommercialAircraft(ControlTower controlTower, String name) {
super(controlTower, name);
}
@Override
public void requestLanding() {
System.out.println(name + " 请求降落");
controlTower.requestLanding(this);
}
@Override
public void performLanding() {
System.out.println(name + " 正在降落...");
// 模拟降落过程
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + " 降落完成");
takeoff(); // 简化逻辑:降落后立即起飞
}
@Override
public void takeoff() {
System.out.println(name + " 请求起飞");
// 模拟起飞准备
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + " 已起飞");
controlTower.notifyTakeoff(this);
}
}
// 使用示例
public class AirportDemo {
public static void main(String[] args) {
ControlTower tower = new AirportControlTower();
Aircraft flight1 = new CommercialAircraft(tower, "CA123");
Aircraft flight2 = new CommercialAircraft(tower, "UA456");
Aircraft flight3 = new CommercialAircraft(tower, "DL789");
flight1.requestLanding();
flight2.requestLanding();
flight3.requestLanding();
}
}
案例二:GUI组件交互
在图形用户界面中,各种组件(按钮、文本框、复选框等)通过中介者进行协调:
// 对话框中介者
interface DialogMediator {
void widgetChanged(Widget widget);
}
class LoginDialog implements DialogMediator {
private Button loginButton;
private TextBox usernameTextBox;
private TextBox passwordTextBox;
private CheckBox rememberMeCheckBox;
public void setLoginButton(Button button) {
this.loginButton = button;
}
public void setUsernameTextBox(TextBox textBox) {
this.usernameTextBox = textBox;
}
public void setPasswordTextBox(TextBox textBox) {
this.passwordTextBox = textBox;
}
public void setRememberMeCheckBox(CheckBox checkBox) {
this.rememberMeCheckBox = checkBox;
}
@Override
public void widgetChanged(Widget widget) {
if (widget == usernameTextBox || widget == passwordTextBox) {
validateForm();
} else if (widget == loginButton) {
performLogin();
}
}
private void validateForm() {
boolean isValid = !usernameTextBox.getText().isEmpty()
&& !passwordTextBox.getText().isEmpty();
loginButton.setEnabled(isValid);
}
private void performLogin() {
String username = usernameTextBox.getText();
String password = passwordTextBox.getText();
boolean rememberMe = rememberMeCheckBox.isChecked();
System.out.println("执行登录: 用户名=" + username +
", 记住我=" + rememberMe);
// 实际的登录逻辑...
}
}
// 基础组件类
abstract class Widget {
protected DialogMediator mediator;
public Widget(DialogMediator mediator) {
this.mediator = mediator;
}
public void changed() {
mediator.widgetChanged(this);
}
}
// 具体组件
class Button extends Widget {
private boolean enabled = true;
public Button(DialogMediator mediator) {
super(mediator);
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
System.out.println("按钮 " + (enabled ? "启用" : "禁用"));
}
public void click() {
if (enabled) {
System.out.println("按钮被点击");
changed();
}
}
}
class TextBox extends Widget {
private String text = "";
public TextBox(DialogMediator mediator) {
super(mediator);
}
public void setText(String text) {
this.text = text;
System.out.println("文本框内容: " + text);
changed();
}
public String getText() {
return text;
}
}
class CheckBox extends Widget {
private boolean checked = false;
public CheckBox(DialogMediator mediator) {
super(mediator);
}
public void setChecked(boolean checked) {
this.checked = checked;
System.out.println("复选框 " + (checked ? "选中" : "未选中"));
changed();
}
public boolean isChecked() {
return checked;
}
}
中介者模式的优缺点
优点
- 降低耦合度:将对象间的一对多关系转变为一对一关系
- 集中控制:将交互逻辑集中在中介者中,便于理解和维护
- 易于扩展:新增同事类只需修改中介者,不影响其他同事类
- 复用性提高:中介者逻辑可以独立复用
缺点
- 中介者可能变得复杂:如果交互关系过于复杂,中介者类可能变得庞大难以维护
- 性能考虑:所有通信都通过中介者,可能成为性能瓶颈
适用场景
中介者模式适用于以下场景:
- 对象间存在复杂的引用关系,系统结构混乱难以理解
- 一个对象引用很多其他对象,直接通信导致复用困难
- 想通过一个中间类封装多个类中的行为,又不想生成太多子类
- 系统中对象间的交互虽有定义但行为复杂,想自定义分布在多个类中的行为
最佳实践
- 合理设计中介者接口:确保接口足够通用,能够适应未来的扩展需求
- 避免上帝对象:不要让中介者承担过多职责,必要时可以引入多个中介者
- 考虑性能优化:对于高频交互场景,可以考虑异步处理或缓存机制
- 测试策略:中介者模式使得单元测试更加容易,可以单独测试各个同事类
总结
中介者模式通过引入一个协调者来管理对象间的复杂交互,有效地降低了系统的耦合度。虽然它可能导致中介者类变得复杂,但在处理多个对象间复杂交互的场景下,中介者模式提供了清晰的解决方案。
在实际项目中,我们应该根据具体需求权衡是否使用中介者模式。当对象间的交互逻辑相对稳定且不会频繁变化时,中介者模式能够显著提升代码的可维护性和可扩展性。
通过本文的讲解和代码示例,相信你已经对中介者模式有了深入的理解。在合适的场景下应用这一模式,将帮助你构建更加清晰、可维护的软件系统。