中介者模式
中介者模式中文讲解
中介者模式(Mediator Pattern)是一种行为型设计模式,属于 GoF(Gang of Four)提出的23种设计模式之一。它的核心目标是通过引入一个中介者对象来封装多个对象之间的交互,降低对象之间的直接耦合,使系统更易于维护和扩展。中介者模式适合处理多个对象复杂交互的场景,例如聊天室、GUI 组件通信等。
以下用中文详细讲解中介者模式的定义、结构、代码示例、应用场景、优缺点以及在 Servlet 环境中的使用,重点突出其原理和实际应用。
1. 什么是中介者模式?
中介者模式通过一个中介者对象协调多个对象(称为同事对象)之间的交互,解决了以下问题:
- 问题:多个对象直接交互会导致紧耦合,形成网状依赖关系,难以修改和扩展。
- 解决方案:引入中介者,所有同事对象通过中介者通信,中介者负责转发和协调请求,降低耦合。
关键特点:
- 集中控制:中介者封装交互逻辑,统一管理通信。
- 解耦:同事对象无需直接引用彼此,仅与中介者交互。
- 灵活性:修改交互逻辑只需调整中介者。
2. 中介者模式的结构
中介者模式包含以下角色:
角色 | 描述 |
---|---|
抽象中介者(Mediator) | 定义中介者接口,声明同事对象通信的方法。 |
具体中介者(Concrete Mediator) | 实现中介者接口,协调同事对象之间的交互,持有同事对象引用。 |
抽象同事(Colleague) | 定义同事类接口,声明与中介者交互的方法。 |
具体同事(Concrete Colleague) | 实现同事接口,通过中介者与其他同事通信。 |
客户端(Client) | 创建中介者和同事对象,设置交互关系。 |
UML 类图(文字描述):
- Mediator:接口,声明方法如
sendMessage()
。 - ConcreteMediator:实现 Mediator,持有 Colleague 引用,协调交互。
- Colleague:接口或抽象类,声明
send()
和receive()
,持有 Mediator 引用。 - ConcreteColleague:实现 Colleague,通过 Mediator 通信。
- 客户端创建 ConcreteMediator 和 ConcreteColleague,设置关联。
3. 代码示例
以下是一个 Java 示例,模拟聊天室,多个用户通过中介者发送消息。
// 抽象中介者
interface ChatMediator {
void sendMessage(String message, User user);
void addUser(User user);
}
// 具体中介者:聊天室
class ChatRoom implements ChatMediator {
private List<User> users = new ArrayList<>();
@Override
public void addUser(User user) {
users.add(user);
}
@Override
public void sendMessage(String message, User sender) {
for (User user : users) {
if (user != sender) { // 消息不发给自己
user.receive(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 receive(String message);
}
// 具体同事:具体用户
class ChatUser extends User {
public ChatUser(ChatMediator mediator, String name) {
super(mediator, name);
}
@Override
public void send(String message) {
System.out.println(name + " 发送: " + message);
mediator.sendMessage(message, this);
}
@Override
public void receive(String message) {
System.out.println(name + " 收到: " + message);
}
}
// 测试
public class MediatorTest {
public static void main(String[] args) {
// 创建中介者
ChatMediator chatRoom = new ChatRoom();
// 创建用户
User user1 = new ChatUser(chatRoom, "张三");
User user2 = new ChatUser(chatRoom, "李四");
User user3 = new ChatUser(chatRoom, "王五");
// 添加用户到聊天室
chatRoom.addUser(user1);
chatRoom.addUser(user2);
chatRoom.addUser(user3);
// 发送消息
user1.send("大家好!");
}
}
输出:
张三 发送: 大家好!
李四 收到: 大家好!
王五 收到: 大家好!
说明:
ChatMediator
是抽象中介者,定义消息发送和用户添加接口。ChatRoom
是具体中介者,管理用户列表,广播消息。User
是抽象同事,定义发送和接收消息的方法。ChatUser
是具体同事,通过中介者发送消息。- 客户端创建聊天室和用户,设置交互关系。
4. 中介者模式的应用场景
- 复杂对象交互:如聊天室、GUI 组件(按钮、文本框交互)。
- 事件驱动系统:如消息队列、事件总线。
- 模块化系统:协调多个模块的通信。
- Servlet 相关:管理多个服务之间的交互(如用户认证和日志服务)。
Servlet 示例:协调用户请求的认证和日志服务。
import javax.servlet.http.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
// 抽象中介者
interface ServiceMediator {
void processRequest(String data, Service service);
void registerService(Service service);
}
// 具体中介者:请求协调器
class RequestMediator implements ServiceMediator {
private List<Service> services = new ArrayList<>();
@Override
public void registerService(Service service) {
services.add(service);
}
@Override
public void processRequest(String data, Service sender) {
for (Service service : services) {
if (service != sender) {
service.handle(data);
}
}
}
}
// 抽象同事:服务
abstract class Service {
protected ServiceMediator mediator;
protected String name;
public Service(ServiceMediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public abstract void process(HttpServletRequest req, HttpServletResponse resp) throws IOException;
public abstract void handle(String data);
}
// 具体同事:认证服务
class AuthService extends Service {
public AuthService(ServiceMediator mediator) {
super(mediator, "认证服务");
}
@Override
public void process(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String user = req.getParameter("user");
if (user != null && !user.isEmpty()) {
System.out.println(name + ": 用户 " + user + " 认证通过");
mediator.processRequest("用户认证: " + user, this);
} else {
resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未提供用户信息");
}
}
@Override
public void handle(String data) {
System.out.println(name + ": 处理数据 - " + data);
}
}
// 具体同事:日志服务
class LoggingService extends Service {
public LoggingService(ServiceMediator mediator) {
super(mediator, "日志服务");
}
@Override
public void process(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String user = req.getParameter("user");
System.out.println(name + ": 记录请求 - 用户 " + user);
mediator.processRequest("日志记录: " + user, this);
}
@Override
public void handle(String data) {
System.out.println(name + ": 处理数据 - " + data);
}
}
// Servlet
public class MediatorServlet extends HttpServlet {
private final ServiceMediator mediator = new RequestMediator();
@Override
public void init() {
// 注册服务
Service authService = new AuthService(mediator);
Service loggingService = new LoggingService(mediator);
mediator.registerService(authService);
mediator.registerService(loggingService);
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=UTF-8");
AuthService authService = new AuthService(mediator);
authService.process(req, resp);
if (!resp.isCommitted()) {
resp.getWriter().write("<h1>请求处理成功</h1>");
}
}
}
输出(访问 /mediator?user=张三
):
认证服务: 用户 张三 认证通过
日志服务: 处理数据 - 用户认证: 张三
<h1>请求处理成功</h1>
说明:
ServiceMediator
是抽象中介者,定义服务协调接口。RequestMediator
协调服务间的交互。Service
是抽象同事,定义处理和交互方法。AuthService
和LoggingService
是具体同事,通过中介者通信。- Servlet 初始化中介者,处理用户请求。
5. 中介者模式的优缺点
优点:
- 解耦:同事对象无需直接交互,降低耦合度。
- 集中控制:交互逻辑集中在中介者,易于管理。
- 灵活性:新增同事对象只需注册到中介者。
- 符合迪米特法则:减少对象间的直接通信。
缺点:
- 中介者复杂性:中介者可能成为“上帝类”,逻辑复杂。
- 性能开销:集中式协调可能增加调用开销。
- 维护成本:中介者变更可能影响多个同事。
6. 注意事项
- 中介者设计:
- 中介者应只负责协调,避免包含过多业务逻辑。
- 保持接口简单,聚焦通信需求。
- 中文编码问题:
- 如果涉及中文(如用户名、日志消息),确保 Servlet 使用 UTF-8:
java resp.setContentType("text/html;charset=UTF-8");
- 对中文参数编码(如
URLEncoder.encode("张三", "UTF-8")
)。
- 线程安全:
- 中介者(如
services
列表)需线程安全(如CopyOnWriteArrayList
)。 - Servlet 环境中,确保同事对象无状态或同步访问。
- 性能优化:
- 缓存同事对象,减少创建开销。
- 优化中介者逻辑,避免复杂循环。
- 与其他模式结合:
- 观察者模式:中介者类似观察者的事件分发。
- 命令模式:封装交互为命令。
- 单例模式:中介者可以是单例。
- 常见问题解决:
- 中介者臃肿:拆分中介者为多个子中介者。
- 通信失败:检查同事是否正确注册到中介者。
- 线程冲突:使用线程安全集合或同步机制。
7. 与其他模式的区别
特性 | 中介者模式 | 观察者模式 | 外观模式 |
---|---|---|---|
目的 | 协调对象交互 | 发布-订阅通知 | 简化子系统接口 |
实现方式 | 集中式中介者 | 主题-观察者 | 封装子系统 |
关注点 | 对象间通信 | 状态变化通知 | 接口简化 |
耦合性 | 降低同事耦合 | 主题与观察者耦合 | 客户端与子系统解耦 |
8. 总结
- 中介者模式通过中介者协调对象交互,降低耦合,适合复杂通信场景。
- 核心是抽象中介者、具体中介者、抽象同事和具体同事。
- 在 Servlet 中,可用于协调服务间的交互(如认证和日志)。
- 优点是解耦和集中控制,缺点是中介者复杂性和性能开销。
- 注意中文编码、线程安全和中介者职责清晰。
如果需要更复杂的示例(如多服务协调、动态中介者)、Servlet 集成细节,或与其他模式(如观察者、命令)的深入对比,请告诉我!