中介者模式 Mediator 概述
什么是中介者
中介者又被称为调停者,调节者,协调者举例说明: 例如QQ发送消息给对方,例如发送短信,我们是将消息发送给QQ这个平台,通过这个平台找到接收者,这个平台就相当于中介角色协调者角色,发送消息与接收消息的双方被称为"同事",又被称为被协调者,例如当接收到某个消息时,协调者进行协调,然后进行其他的处理等,而中介者中进行逻辑处理的方法被称为协调方法
中介者模式的优点
中介者模式是行为型设计模式, 在项目功能架构时,添加中介者对象,通过中介者来控制一系列对象之间的交互,减少各个对象之间显示的相互引用,从而降低耦合,并且可以在中介者中定制他们的交互行为,降低多个对象间通信的复杂性
中介者模式角色分析
- 抽象中介者 Mediator : 提供将同事对象存入中介者容器中的接口,与接收同事对象发送的消息接口也叫做协调方法
- 具体中介者 ConcreteMediator : 持有所有需要通信的同事对象容器,实现向容器中存放通信对象的方法,与接收同事消息的方法,接收到消息后,根据需求,将消息发送给容器中的指定接收同事对象,或者中介者接收到消息后,进行指定的逻辑处理等
- 抽象同事 Colleague : 持有中介者对象,当同事对象发送消息时,实际是通过持有的中介者对象,调用中介者方法,将消息发送给了中介者,所有通信同事都需要存放在中介者的容器中,所以提供构造器,在调用构造器进行初始化同事对象时,就将当前同事对象this,通过持有的中介者,调用方法,存入容器中
- 具体同事 ConcreteColleague : 继承抽象同事,重写发送消息方法,当发送消息时根据需求编写具体的逻辑代码,重点是通过持有的中介者,将消息发送给中介者,通过中介者对消息进行处理
示例
案例: 通过闹铃定时,某个时刻闹铃发送发送消息,咖啡机煮咖啡,打开电视,播放电视,当闹铃响再次响起时发送消息,关闭电视…
- 创建抽象中介者 receptionMessage() 方法就是协调方法
interface Mediator {
//将通信同事对象,加入到中介者用来存放通信同事的容器集合中
public void register(Colleague colleague);
//接收同事发送的消息,根据需求在该方法中编写逻辑代码,
//将消息发送给指定的接收同事,做到转发与调解的作用
public void receptionMessage(int stateChange, Colleague colleague);
}
- 创建具体中介者,具体中介者中持有所有同事容器,并实现接收到同事消息后怎么处理消息的协调方法方法receptionMessage()
//具体的中介者
class ConcreteMediator implements Mediator {
//存放了发送消息,与接收消息的同事
private HashMap<String, Colleague> colleagueMap;
public ConcreteMediator() {
colleagueMap = new HashMap<>();
}
@Override
public void register(Colleague colleague) {
colleagueMap.put(colleague.name, colleague);
}
@Override
public void receptionMessage(int stateChange, Colleague colleague) {
//1.获取容器中的同事
Object obj = colleagueMap.get(colleague.name);
//当是闹铃时判断消息,如果消息是"0" 开始煮咖啡
//播放电视,当消息是"1"时,停止播放电视
if (obj instanceof Alarm) {
if (stateChange == 0) {
((CoffeeMachine) (colleagueMap.get("CoffeeMachine"))).startCoffee();
((TV) (colleagueMap.get("TV"))).startTV();
} else if (stateChange == 1) {
((TV) (colleagueMap.get("TV"))).stopTV();
}
//2.当在容器中获取的同事是咖啡机时,升起窗帘
} else if (colleagueMap.get(colleague.name) instanceof CoffeeMachine) {
((Curtains) (colleagueMap.get("Curtains"))).upCurtains();
} else if (colleagueMap.get(colleague.name) instanceof TV) {
} else if (colleagueMap.get(colleague.name) instanceof Curtains) {
}
}
}
- 创建抽象同事,抽象同事持有中介者对象,
abstract class Colleague {
public Mediator mediator;
public String name;
public Colleague(Mediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
//返回持有的中介者
public Mediator getMediator() {
return this.mediator;
}
//发送消息的抽象方法(根据需求在子类中实现,重点:在中介者模式中
//是将消息发送给了中介者,通过中介者将消息发送给指定的接收者)
public abstract void sendMessage(int stateChange);
}
- 创建具体同事: 具体同事可能存在多个,通信双方等,所有同事对象初始化后都要放在中介者的容器中,根据需求实现发送消息方法,但是最终是将主要消息通过持有的中介者对象,将消息发送给了中介者,通过中介者进行调解处理
具体同事"闹铃"
class Alarm extends Colleague {
public Alarm(Mediator mediator, String name) {
super(mediator, name);
mediator.register(this);
}
public void sendAlarm(int stateChange) {
System.out.println("闹了响起,到点了");
sendMessage(stateChange);
}
//闹铃发送消息实现,实际是通过持有的中介者对象,
//调用中介者的receptionMessage()方法,通过中介者
//进行指定处理:例如中介者接收到这个消息以后,
//进行其他操作,再例如中介者接收到消息以后,通过
//中介者,获取容器中的同事对象,将消息进行转发
@Override
public void sendMessage(int stateChange) {
//调用中介者对象的receptionMessage()方法
this.getMediator().receptionMessage(stateChange, this);
}
}
咖啡机
class CoffeeMachine extends Colleague {
public CoffeeMachine(Mediator mediator, String name) {
super(mediator, name);
mediator.register(this);
}
public void sendAlarm(int stateChange) {
sendMessage(stateChange);
}
@Override
public void sendMessage(int stateChange) {
this.getMediator().receptionMessage(stateChange, this);
}
public void startCoffee() {
System.out.println("咖啡机开始煮咖啡");
}
public void finishCoffee(){
System.out.println("咖啡煮好了");
sendMessage(0);
}
}
电视
//具体同事类"TV"
class TV extends Colleague {
public TV(Mediator mediator, String name) {
super(mediator, name);
mediator.register( this);
}
public void sendAlarm(int stateChange) {
sendMessage(stateChange);
}
@Override
public void sendMessage(int stateChange) {
this.getMediator().receptionMessage(stateChange, this);
}
public void startTV() {
System.out.println("打开电视,开始播放");
}
public void stopTV(){
System.out.println("关闭电视,停止播放");
}
}
窗帘
class Curtains extends Colleague {
public Curtains(Mediator mediator, String name) {
super(mediator, name);
mediator.register(this);
}
public void sendAlarm(int stateChange) {
sendMessage(stateChange);
}
@Override
public void sendMessage(int stateChange) {
this.getMediator().receptionMessage(stateChange, this);
}
public void upCurtains() {
System.out.println("打开窗帘");
}
}
- 调用测试
public static void main(String[] args) {
//创建中介者对象
Mediator mediator = new ConcreteMediator();
//通信时需要双方都存在,然后通过中介者,进行协调调用
//所以首先要将需要通信的对象初始化,放入中介者容器中
//创建Alarm闹钟具体同事对象
Alarm alarm = new Alarm(mediator, "Alarm");
//创建CoffeeMachine咖啡机具体同事对象
CoffeeMachine coffeeMachine = new CoffeeMachine(mediator, "CoffeeMachine");
//创建窗帘具体同事对象
Curtains curtains = new Curtains(mediator, "Curtains");
//创建电视具体同事对象
TV tv = new TV(mediator, "TV");
alarm.sendAlarm(0);
//coffeeMachine.finishCoffee();
alarm.sendAlarm(1);
}
根据代码分析理解
我理解的是,所有发送接收消息的同事都持有同一个中介者,在初始化同事对象时,将这个同事对象本身this,通过持有的中介者对象,放入中介者容器中,这样所有的发送消息,的同事对象都存入了中介者容器中,当同事发送消息时,实际是通过持有的中介者对象,将消息发送给了中介者,中介者接收到消息后,在中介者中进行逻辑处理,例如接收到闹铃的消息"0"时煮咖啡,播放电视,接收到闹铃的消息"1"时关闭电视,接收到咖啡消息时窗帘上升,在例如将获取消息,将消息转发给容器中指定的其他同事等,这样所有的同事只与中介者打交道,减少同事与同事之间相互调用的复杂关系
JDK 中 中介者模式的使用案例
查看jdk中 java.util.Timer,计时器相关的类,该类可以看为是一个中介者,该类中的sched()方法可以看为是协调方法,方法中的 TimerTask 可以看为同事类,被协调者
业务与设计模式落地案例
- 通过中介者模式来实现用户之间的消息传递和沟通
- 定义中介者接口,包含向指定用户发送消息、向所有用户广播消息、添加用户、移除用户等方法
public interface ChatMediator {
void sendMessage(User sender, User receiver, String message);
void broadcastMessage(User sender, String message);
void addUser(User user);
void removeUser(User user);
}
- 定义具体的中介者实现类,该类维护了一个用户列表,管理所有的聊天用户,当用户发送消息或广播消息时,中介者将相应的消息传递给目标用户或所有用户
@AllArgsConstructor
public class ChatMediatorImpl implements ChatMediator {
private final List<User> users;
@Override
public void sendMessage(User sender, User receiver, String message) {
receiver.receiveMessage(sender, message);
}
@Override
public void broadcastMessage(User sender, String message) {
for (User user : users) {
if (user != sender) {
user.receiveMessage(sender, message);
}
}
}
@Override
public void addUser(User user) {
users.add(user);
}
@Override
public void removeUser(User user) {
users.remove(user);
}
}
- 定义用户对象,包含用户ID、名称、中介者等属性
@Data
@AllArgsConstructor
public class User {
private Long id;
private String name;
private ChatMediator mediator;
//发送消息
public void sendMessage(User receiver, String message) {
mediator.sendMessage(this, receiver, message);
}
//广播消息
public void broadcastMessage(String message) {
mediator.broadcastMessage(this, message);
}
//处理接收到的消息
public void receiveMessage(User sender, String message) {
System.out.println("[收到消息] " + sender.getName() + ": " + message);
}
}
- Controller中实现用户聊天的业务逻辑
@RestController
@RequestMapping("/chat")
@AllArgsConstructor
public class ChatController {
private final ChatMediator mediator;
//向指定用户发送消息
@PostMapping("/{senderId}/send")
public String sendMessage(@PathVariable Long senderId, @RequestParam Long receiverId, @RequestParam String message) {
User sender = getUserById(senderId);
User receiver = getUserById(receiverId);
sender.sendMessage(receiver, message);
return "发送成功";
}
//向所有用户广播消息
@PostMapping("/{senderId}/broadcast")
public String broadcastMessage(@PathVariable Long senderId, @RequestParam String message) {
User sender = getUserById(senderId);
sender.broadcastMessage(message);
return "发送成功";
}
private User getUserById(Long userId) {
// 省略获取用户信息的代码
return new User(userId, "User " + userId, mediator);
}
}
- 通过这种方式,可以将用户之间的聊天行为完全解耦,所有的消息传递都通过ChatMediator对象进行,避免了直接耦合引起的系统复杂度过高的问题,还可以很方便地管理所有的聊天用户,并随时添加,删除或修改中介者对象和用户对象,不需要对现有代码进行修改