1. 概念
- 中介者模式(Mediator Pattern),是一种对象行为型模式。该模式的主要目的是定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合变得松散,并且可以独立地改变它们之间的交互。
2. 原理结构图
- 抽象中介者(Mediator):这是中介者模式的核心接口,它定义了同事对象注册和信息转发的抽象方法。这个角色的主要职责是声明并规范中介者的操作,以便具体中介者能够实现这些操作来协调同事对象之间的交互。
- 具体中介者(Concrete Mediator):这个角色实现了抽象中介者接口,并具体定义了一个管理同事对象的列表。它负责协调各个同事角色之间的交互关系,因此依赖于同事角色。
- 抽象同事类(Colleague):这个角色定义了同事类的接口,它包含了保存中介者对象的方法和提供同事对象交互的抽象方法。这个角色的目的是实现所有相互影响的同事类的公共功能。
- 具体同事类(Concrete Colleague):这是抽象同事类的实现者。当具体同事类需要与其他同事对象交互时,它会通过中介者对象来进行后续的交互,而不是直接与其他同事对象通信。
3. 代码示例
3.1 示例1-聊天室系统
- 在这个场景中,每个用户(客户端)都可以看作是一个同事类,而聊天室服务器则充当中介者的角色。用户之间不需要直接通信,而是通过服务器来转发消息。
java
import java.util.ArrayList;
import java.util.List;
// 聊天室服务器,作为中介者
class ChatRoom {
private List<User> users = new ArrayList<>();
// 用户加入聊天室
public void registerUser(User user) {
users.add(user);
user.setChatRoom(this);
System.out.println(user.getName() + " has joined the chat room.");
}
// 用户离开聊天室
public void unregisterUser(User user) {
users.remove(user);
System.out.println(user.getName() + " has left the chat room.");
}
// 发送消息给所有用户
public void broadcast(User sender, String message) {
for (User user : users) {
if (!user.equals(sender)) {
user.receive(sender.getName() + ": " + message);
}
}
}
}
// 用户(同事类)
class User {
private String name;
private ChatRoom chatRoom;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setChatRoom(ChatRoom chatRoom) {
this.chatRoom = chatRoom;
}
// 用户发送消息
public void send(String message) {
chatRoom.broadcast(this, message);
}
// 用户接收消息
public void receive(String message) {
System.out.println(this.name + " received: " + message);
}
}
// 聊天室客户端模拟
public class ChatRoomDemo {
public static void main(String[] args) {
ChatRoom chatRoom = new ChatRoom();
User user1 = new User("Alice");
User user2 = new User("Bob");
User user3 = new User("Charlie");
chatRoom.registerUser(user1);
chatRoom.registerUser(user2);
chatRoom.registerUser(user3);
// Alice 发送消息
user1.send("Hello everyone!");
// Bob 发送消息
user2.send("Hi Alice, how are you?");
// Charlie 离开聊天室
chatRoom.unregisterUser(user3);
// Alice 再次发送消息,Charlie 不再接收
user1.send("Charlie, are you there?");
}
}
- 将看到如下输出:
java
Alice has joined the chat room.
Bob has joined the chat room.
Charlie has joined the chat room.
Bob received: Alice: Hello everyone!
Charlie received: Alice: Hello everyone!
Alice received: Bob: Hi Alice, how are you?
Charlie received: Bob: Hi Alice, how are you?
Charlie has left the chat room.
Bob received: Alice: Charlie, are you there?
- 在这个例子中,ChatRoom 类充当了中介者的角色,它维护了一个用户列表,并提供了注册用户、注销用户以及广播消息的方法。User 类代表了聊天室中的用户,每个用户都有一个名字,并且可以发送和接收消息。用户通过调用 send 方法来发送消息,而消息则通过 ChatRoom 的 broadcast 方法广播给所有其他用户。
- 在 main 方法中,我们创建了一个 ChatRoom 实例和三个 User 实例,并模拟了用户加入聊天室、发送消息以及离开聊天室的过程。
- 这个简单的例子展示了中介者模式在聊天室系统中的应用,通过中介者(聊天室服务器)来管理用户之间的通信,降低了用户之间的耦合度,使得系统更加灵活和易于维护。在实际应用中,聊天室系统会更加复杂,可能需要处理更多的功能,如用户身份验证、消息历史记录、私有聊天等,但中介者模式的基本思想仍然适用。
3.2 示例2-智能家居系统
- 在智能家居系统中,中介者模式可以应用于管理各种智能设备之间的交互。这些设备可能包括灯光、空调、电视、窗帘等,每个设备都作为同事类,而智能家居中心或服务器则作为中介者,负责协调和管理设备之间的通信和交互。
java
import java.util.HashMap;
import java.util.Map;
// 智能家居系统的中介者,负责管理设备间的交互
class SmartHomeCenter {
private Map<String, SmartDevice> devices = new HashMap<>();
// 注册设备
public void registerDevice(SmartDevice device) {
devices.put(device.getName(), device);
device.setSmartHomeCenter(this);
}
// 注销设备
public void unregisterDevice(SmartDevice device) {
devices.remove(device.getName());
}
// 接收设备发送的命令并转发给相关设备
public void receiveCommand(SmartDevice sender, String command, String target) {
SmartDevice targetDevice = devices.get(target);
if (targetDevice != null && !targetDevice.equals(sender)) {
targetDevice.receiveCommand(sender, command);
} else {
System.out.println("Target device not found or is the sender itself.");
}
}
}
// 智能设备接口
interface SmartDevice {
String getName();
void setSmartHomeCenter(SmartHomeCenter center);
void sendCommand(String command, String target);
void receiveCommand(SmartDevice sender, String command);
}
// 具体的智能设备类,如灯光
class Light implements SmartDevice {
private String name;
private SmartHomeCenter center;
public Light(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void setSmartHomeCenter(SmartHomeCenter center) {
this.center = center;
}
@Override
public void sendCommand(String command, String target) {
center.receiveCommand(this, command, target);
}
@Override
public void receiveCommand(SmartDevice sender, String command) {
System.out.println(name + " received command from " + sender.getName() + ": " + command);
// 执行相应的命令逻辑,如开关灯等
}
}
// 假设的空调类,实现SmartDevice接口
class AirConditioner implements SmartDevice {
private String name;
private SmartHomeCenter center;
public AirConditioner(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void setSmartHomeCenter(SmartHomeCenter center) {
this.center = center;
}
@Override
public void sendCommand(String command, String target) {
center.receiveCommand(this, command, target);
}
@Override
public void receiveCommand(SmartDevice sender, String command) {
System.out.println(name + " received command from " + sender.getName() + ": " + command);
// 执行相应的命令逻辑,如开关空调、调节温度等
}
}
// 智能家居系统客户端模拟
public class SmartHomeDemo {
public static void main(String[] args) {
SmartHomeCenter center = new SmartHomeCenter();
SmartDevice light1 = new Light("Living Room Light");
SmartDevice ac1 = new AirConditioner("Living Room AC");
center.registerDevice(light1);
center.registerDevice(ac1);
// 客厅灯光发送命令给空调,要求其关闭
light1.sendCommand("turnOff", ac1.getName());
// 注销设备
center.unregisterDevice(light1);
// 再次尝试发送命令,由于灯光已注销,将不会执行
light1.sendCommand("turnOn", ac1.getName());
}
}
- 将看到如下输出:
java
Living Room AC received command from Living Room Light: turnOff
Living Room AC received command from Living Room Light: turnOn
- SmartHomeDemo 类中,我们模拟了智能家居系统的客户端行为。首先,我们创建了 SmartHomeCenter 实例和两种智能设备实例 Light 和 AirConditioner,并将它们注册到中介者中。接着,我们通过调用 Light 实例的 sendCommand 方法来模拟发送命令给 AirConditioner 实例,要求其关闭。因为中介者负责协调设备间的通信,所以命令通过中介者正确地发送到了 AirConditioner。
- 最后,我们注销了 Light 实例,并尝试再次发送命令。由于该设备已被注销,中介者不会将命令转发给 AirConditioner,从而模拟了设备离线或未注册的情况。
- 这只是一个非常简单的示例,真实的智能家居系统会更复杂,涉及更多的设备类型、通信协议、安全机制以及用户界面交互等。但通过这个例子,你应该能够理解中介者模式在智能家居系统中如何工作,以及它是如何简化设备间通信和交互的。
- 在实际应用中,你还需要考虑设备之间的异步通信、错误处理、设备状态管理以及与其他系统的集成(如语音助手、移动应用等)。此外,对于大规模部署和复杂的家居环境,可能需要更高级的架构设计和性能优化。
4. 优缺点
- 主要作用
- 降低对象之间的耦合性,并简化它们之间的通信。
- 优点
- 实现解耦合:各个对象不再直接相互依赖,而只依赖于中介者,这显著降低了类之间的耦合性。
- 简化交互:中介者模式简化了对象之间的交互过程。在复杂的系统中,对象之间可能存在大量的交互关系,如果直接进行通信,将导致代码复杂且难以维护。通过中介者进行集中管理,可以将复杂的交互逻辑封装在中介者内部,简化了对象之间的通信过程,使得代码更加清晰和易于理解。
- 可扩展性:中介者模式使得系统更加易于扩展。当需要添加新的对象或新的交互关系时,只需要修改中介者的代码,而无需修改其他对象的代码。这降低了系统的扩展难度,提高了系统的可扩展性。
- 统一控制:中介者模式实现了对交互关系的统一控制。通过中介者,可以对对象之间的交互进行全局的管理和协调,确保交互的正确性和一致性。这有助于避免由于对象之间的直接通信而导致的混乱和错误。
- 缺点
- 中介者可能变得过于复杂:随着系统中同事类数量的增加,中介者需要管理的交互关系也会变得更为复杂。这可能导致中介者类的逻辑变得庞大且难以维护,增加了系统的复杂性。
- 可能导致系统性能下降:由于所有对象间的交互都需要通过中介者进行,当交互频繁或涉及大量数据时,中介者可能成为性能瓶颈,导致系统响应速度变慢。
- 降低了系统的透明度:由于交互逻辑被封装在中介者内部,外部对象无法直接了解其他对象的交互细节。这降低了系统的透明度,使得理解和调试系统变得更加困难。
- 对中介者的依赖性强:同事类依赖于中介者进行交互,如果中介者出现问题或无法正常工作,整个系统可能会受到影响。这增加了系统的风险,需要确保中介者的稳定性和可靠性。
5. 应用场景
5.1 主要包括以下几个方面
- 对象关系复杂:当系统中的对象之间存在复杂的引用关系,导致结构混乱难以理解时,引入中介者可以简化这些关系。
- 行为变更频繁:如果系统需要频繁地改变对象间的交互行为,可以通过增加新的中介者类来实现,这样有助于保持系统的灵活性。
- 多人交互场景:例如多人聊天室,一个人发言需要传达给每个人,如果没有聊天室(中介者),则需要对每个人都说一遍;有了中介者,就由它负责传达,减少了个体之间的直接交互。
- 系统解耦要求高:在需要降低对象间直接依赖,提高系统模块独立性的情况下,中介者模式是一个很好的选择。
- 复杂系统设计:对于复杂的系统如GUI系统、企业应用程序等,使用中介者模式可以使系统更易于维护和扩展。
5.2 实际应用
- 聊天室系统:在一个聊天室应用中,用户之间的消息传递不需要直接相互引用。通过中介者,即聊天室服务器,来协调消息的发送和接收,这样每个用户只需与服务器交互即可。
- 微服务架构:在微服务架构中,不同的服务之间通过一个中介者来进行通信,这个中介者通常是服务总线或者消息队列,它负责传递和路由消息,降低了服务间的直接依赖。
- 智能家居系统:在智能家居系统中,各种设备如灯光、温度控制器、安全摄像头等,可以通过一个中心控制系统来协调它们的工作,而不是直接相互通信。
- 多人游戏:在多人在线游戏中,玩家之间的互动(如聊天、交易、组队等)可以通过一个游戏服务器来中介,这样可以避免玩家之间直接建立连接,减少了系统的复杂性。
6. JDK中的使用
- java.util.Timer 类
- 中介者角色(Timer)
- Timer类提供了注册任务(通过schedule方法)和取消任务的方法,这些方法允许同事类(即需要定时执行的任务)与中介者进行交互。
- Timer内部维护了一个任务队列,并负责按照指定的时间间隔或延迟来执行这些任务。它协调了各个任务之间的执行顺序和时间,确保它们能够按照预期的方式运行。
- 同事类角色(TimerTask 或其他实现 Runnable 接口的类)
- 这些类表示需要定时执行的任务,它们实现了Runnable接口或继承了TimerTask类,并定义了任务的具体执行逻辑。
- 同事类通过调用Timer类的schedule方法将自己注册到中介者中,并指定执行的时间和周期。一旦注册成功,它们就不再需要直接与其他任务进行交互,而是依赖于Timer中介者来管理和调度它们的执行。
- 中介者角色(Timer)
7. 注意事项
- 避免过度集中控制:虽然中介者有助于降低系统的耦合度,但过度依赖中介者可能导致中介者类的职责过重,增加维护难度。
- 保持中介者的简洁性:中介者应专注于协调和转发消息,避免在其中引入过多的业务逻辑。
- 适时重构:随着系统的发展,中介者可能会变得复杂,需要定期审视并重构以保持系统的清晰和可维护性。
- 性能考量:在某些高性能需求的场景中,中介者可能成为性能瓶颈,需注意性能优化。
- 扩展性设计:设计时应考虑未来可能的变化,确保中介者模式的实现能够适应新的需求和变化。
8. 中介者模式 VS 组合模式
模式 | 类型 | 目的 | 模式架构主要角色 | 应用场景 |
---|---|---|---|---|
中介者模式 | 行为型 | 简化对象间复杂交互,降低耦合度 | 中介者、同事类 | 对象间交互复杂且需要解耦时 |
组合模式 | 结构型 | 统一处理组合对象与单个对象,表达层次结构 | 组合对象、叶子对象 | 需要表示部分-整体层次结构时 |