一、中介者模式概述
中介者模式(Mediator Pattern),也称为调解者模式或调停者模式,是一种行为设计模式。它的核心思想是通过引入一个中介者对象来封装一系列对象之间的交互,使得这些对象不必直接相互作用,从而达到松散耦合的效果。这种设计模式主要用于降低系统中对象之间的耦合度,提高系统的灵活性和可维护性。
中介者模式定义了一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,并且可以独立地改变它们之间的交互。在这个模式中,各个对象不再直接相互引用和通信,而是通过中介者来进行间接通信。这样,不仅降低了对象之间的依赖关系,还简化了对象之间的交互逻辑。
二、中介者模式的结构
中介者模式通常包含以下几个关键角色:
- 抽象中介者(Mediator):定义一个接口,该接口用于定义各同事(Colleague)对象之间进行通信需要的方法。
- 具体中介者(ConcreteMediator):实现抽象中介者接口,通过协调各个同事对象来实现协作行为,了解并维护它的各个同事,它维持了对各个同事对象的引用。
- 抽象同事类(Colleague):定义各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时它维持了一个对抽象中介者类的引用,其子类可以通过该引用来与中介者通信。
- 具体同事类(ConcreteColleague):实现抽象同事类,每一个同事对象在需要和其他同事对象通信时,先与中介者通信,通过中介者来间接完成与其他同事类的通信。
三、中介者模式的理解
- 降低耦合度:通过引入中介者对象,各个对象之间的直接依赖关系被减少,使得系统结构更加灵活和可扩展。
- 简化交互:中介者模式将对象之间的多对多交互关系转换为中介者与各个对象之间的一对多关系,简化了对象之间的交互逻辑。
- 易于维护和扩展:由于对象之间的交互被集中到中介者对象中,因此当需要修改或扩展对象之间的交互时,只需修改中介者对象,而不需要修改各个对象的代码。
然而,中介者模式也有其局限性。如果系统中存在大量的同事类,中介者可能会变得非常复杂,难以维护。因此,在实际应用中需要权衡利弊,根据具体情况决定是否采用该模式。
四、中介者模式的实践
下面我们通过具体的Java代码示例来展示中介者模式的实践应用。
示例一:简单的聊天系统
假设我们要设计一个可以让多人参与进去的聊天室,该聊天室需要实现发送消息和新增用户的功能。
首先,我们定义聊天室的接口ChatRoom
,其中包含两个方法sendMessage
和addUser
,分别代表发送消息和新增用户。这里的ChatRoom
就是抽象的中介者类。
public interface ChatRoom {
void sendMessage(String msg, String userId);
void addUser(User user);
}
然后,我们创建一个ChatRoom
的实现类ChatRoomImpl
,使用addUser
来添加需要聊天的用户对象,同时这里再使用一个Map
来保存添加时需要用来进行通信的对象列表。在发送消息sendMessage
的方法中,我们通过userId
指定某个对象来接收消息。
import java.util.HashMap;
import java.util.Map;
public class ChatRoomImpl implements ChatRoom {
private Map<String, User> usersMap = new HashMap<>();
@Override
public void sendMessage(String msg, String userId) {
User u = usersMap.get(userId);
u.receive(msg);
}
@Override
public void addUser(User user) {
this.usersMap.put(user.getId(), user);
}
}
接下来,我们定义一个抽象组件类User
,它持有一个ChatRoom
的引用,并声明了发送消息send
和接收消息receive
的抽象方法。
public abstract class User {
private ChatRoom mediator;
private String id;
private String name;
public User(ChatRoom room, String id, String name) {
this.mediator = room;
this.name = name;
this.id = id;
}
public abstract void send(String msg, String userId);
public abstract void receive(String msg);
public ChatRoom getMediator() {
return mediator;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
然后,我们实现一个具体的组件类ChatUser
,并实现发送消息send
和接收消息receive
的方法。
public class ChatUser extends User {
public ChatUser(ChatRoom room, String id, String name) {
super(room, id, name);
}
@Override
public void send(String msg, String userId) {
getMediator().sendMessage(msg, userId);
}
@Override
public void receive(String msg) {
System.out.println(getName() + "收到消息: " + msg);
}
}
最后,我们在客户端代码中创建中介者和同事对象的实例,并将它们关联起来。
public class ChatDemo {
public static void main(String[] args) {
ChatRoom chatRoom = new ChatRoomImpl();
User user1 = new ChatUser(chatRoom, "1", "张三");
User user2 = new ChatUser(chatRoom, "2", "李四");
chatRoom.addUser(user1);
chatRoom.addUser(user2);
user1.send("你好, 李四", "2");
user2.send("你好, 张三", "1");
}
}
运行上述代码,输出结果如下:
李四收到消息: 你好, 李四
张三收到消息: 你好, 张三
通过这个示例,我们可以看到,聊天系统中的用户对象(User
)通过中介者(ChatRoom
)来进行间接通信,而不需要直接引用对方。这样,不仅降低了用户对象之间的耦合度,还简化了用户之间的交互逻辑。
示例二:多架飞机降落
假设有多架飞机需要跑道降落,塔台作为中介者指挥飞机降落。每一架飞机都是一个组件,拥有一些相同的行为,塔台作为中介者负责指挥飞机的降落。
首先,我们定义抽象中介者接口TowerMediator
,它包含通知方法notify
、注册方法register
和移除方法remove
。
public interface TowerMediator {
void notify(String type, String track);
void register(AirplaneComponent colleague);
void remove(AirplaneComponent colleague);
}
然后,我们实现具体的中介者类AirlinerConcreteMediator
,它持有一个List
来管理组件对象(即飞机)。
import java.util.ArrayList;
import java.util.List;
public class AirlinerConcreteMediator implements TowerMediator {
private List<AirplaneComponent> colleagues = new ArrayList<>();
@Override
public void notify(String type, String track) {
for (AirplaneComponent airplaneComponent : colleagues) {
if (!type.equalsIgnoreCase(airplaneComponent.getAirplaneType())) {
continue;
}
airplaneComponent.landing(track);
}
}
@Override
public void register(AirplaneComponent colleague) {
colleagues.add(colleague);
}
@Override
public void remove(AirplaneComponent colleague) {
colleagues.remove(colleague);
}
}
接下来,我们定义抽象组件类AirplaneComponent
,它持有一个中介者对象的引用,并声明了一个抽象方法landing
。
public abstract class AirplaneComponent {
public String airplaneType;
private TowerMediator towerMediator;
public AirplaneComponent(String airplaneType, TowerMediator towerMediator) {
this.airplaneType = airplaneType;
this.towerMediator = towerMediator;
}
public abstract void landing(String track);
public TowerMediator getTowerMediator() {
return towerMediator;
}
}
然后,我们实现具体的组件类AirlinerConcreteComponent
,它代表客机。
public class AirlinerConcreteComponent extends AirplaneComponent {
public AirlinerConcreteComponent(String airplaneType, TowerMediator towerMediator) {
super(airplaneType, towerMediator);
}
@Override
public void landing(String track) {
System.out.println(airplaneType + "正在跑道" + track + "上降落");
}
}
最后,我们在客户端代码中创建中介者和组件对象的实例,并将它们关联起来。
// 客户端代码
public class AirportDemo {
public static void main(String[] args) {
// 创建中介者(塔台)
TowerMediator towerMediator = new AirlinerConcreteMediator();
// 创建具体的组件(飞机),并注册到中介者
AirplaneComponent airplane1 = new AirlinerConcreteComponent("波音737", towerMediator);
AirplaneComponent airplane2 = new AirlinerConcreteComponent("空客A320", towerMediator);
AirplaneComponent airplane3 = new AirlinerConcreteComponent("波音787", towerMediator);
// 通知中介者进行降落安排(这里假设有两个跑道:Runway 1 和 Runway 2)
towerMediator.notify("波音737", "Runway 1");
towerMediator.notify("空客A320", "Runway 2");
towerMediator.notify("波音787", "Runway 1"); // 假设波音787可以等待或使用另一个空闲跑道
// 注意:由于中介者内部已经维护了组件的列表,因此不需要显式地移除组件,
// 除非在程序的某个阶段需要动态地卸载组件。
}
}
在这个示例中,AirportDemo
类的main
方法是客户端代码,它创建了中介者AirlinerConcreteMediator
的实例,并创建了多个具体的组件(飞机)实例,这些实例都被注册到了中介者中。然后,通过调用中介者的notify
方法,模拟了飞机请求降落的过程,并分配了跑道。
运行AirportDemo
类的main
方法,您将看到类似以下的输出:
波音737 正在跑道 Runway 1 上降落
空客A320 正在跑道 Runway 2 上降落
波音787 正在跑道 Runway 1 上降落