中介者模式的一些关键组件:
- 中介者(Mediator):定义了一个接口,用于与各个同事(Colleague)对象通信。
- 具体中介者(Concrete Mediator):实现中介者接口并协调各个同事对象之间的交互。
- 同事(Colleague):定义了各个对象之间的接口。每个同事对象知道它的中介者对象,但不知道其他同事对象。
- 具体同事(Concrete Colleague):实现同事接口并通过中介者与其他同事对象通信。
实际上,中介模式的设计思想跟中间层很像,通过引入中介这个中间层,将一组对象之间的交互关系(或者说依赖关系)从多对多(网状关系)转换为一对多(星状关系)。原来一个对象要跟 n 个对象交互,现在只需要跟一个中介对象交互,从而最小化对象之间的交互关系,降低了代码的复杂度,提高了代码的可读性和可维护性。
下面是一个简单的Java代码实例:
interface Mediator {
void send(String message, Colleague colleague);
}
// 具体中介者
class ConcreteMediator implements Mediator {
private Colleague1 colleague1;
private Colleague2 colleague2;
public void setColleague1(Colleague1 colleague1) {
this.colleague1 = colleague1;
}
public void setColleague2(Colleague2 colleague2) {
this.colleague2 = colleague2;
}
@Override
public void send(String message, Colleague colleague) {
if (colleague == colleague1) {
colleague2.notify(message);
} else {
colleague1.notify(message);
}
}
}
// 同事接口
abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public abstract void send(String message);
public abstract void notify(String message);
}
// 具体同事1
class Colleague1 extends Colleague {
public Colleague1(Mediator mediator) {
super(mediator);
}
@Override
public void send(String message) {
mediator.send(message, this);
}
@Override
public void notify(String message) {
System.out.println("Colleague1 receives message: " + message);
}
}
// 具体同事2
class Colleague2 extends Colleague {
public Colleague2(Mediator mediator) {
super(mediator);
}
@Override
public void send(String message) {
mediator.send(message, this);
}
@Override
public void notify(String message) {
System.out.println("Colleague2 receives message: " + message);
}
}
// 客户端代码
public class MediatorPatternDemo {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
Colleague1 colleague1 = new Colleague1(mediator);
Colleague2 colleague2 = new Colleague2(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.send("Hello, Colleague2!");
colleague2.send("Hi, Colleague1! How are you?");
colleague1.send("I'm fine, thank you!");
colleague2.send("Great, have a nice day!");
}
}
在这个例子中,让Colleague1
和Colleague2
对象之间互相发送消息。在主函数中,创建了一个ConcreteMediator
对象,并将其分别传递给Colleague1
和Colleague2
对象。然后使用中介者对象将两个同事对象关联起来。最后让对象互相发送消息。
输出结果如下:
Colleague2 receives message: Hello, Colleague2!
Colleague1 receives message: Hi, Colleague1! How are you?
Colleague2 receives message: I'm fine, thank you!
Colleague1 receives message: Great, have a nice day!
通过使用中介者模式,实现了Colleague1
和Colleague2
对象之间的解耦。如果需要在系统中引入更多的同事对象,只需创建一个新的具体同事类并在中介者中实现适当的逻辑即可。这大大降低了对象之间的耦合度,提高了代码的可维护性和可扩展性。
使用场景
中介模式有哪些使用场景:
- 聊天室:在一个聊天室应用中,用户之间需要相互发送消息。使用中介者模式,可以创建一个中介者对象(聊天室服务器)来管理用户之间的消息传递,这样每个用户不需要知道其他用户的存在,只需将消息发送给中介者,然后由中介者负责分发消息。
- 机场调度系统:机场调度系统通常需要管理多个飞机、跑道和门等资源。在这种情况下,可以使用一个中介者(如调度塔)来协调和管理这些资源之间的通信和安排,以确保系统的正常运行。
- 图形用户界面(GUI)组件:在复杂的GUI应用程序中,各种组件(如按钮、列表框、文本框等)可能需要相互通信。使用中介者模式,可以创建一个中介者对象来处理这些组件之间的通信,避免组件之间直接相互引用,降低它们之间的耦合度。
- 智能家居系统:在智能家居系统中,各种设备(如灯、窗帘、空调等)需要根据用户的需求进行协调和控制。通过引入一个中介者(如智能家居中心),可以实现各种设备之间的协调和通信,从而实现对整个系统的统一管理。
- MVC(Model-View-Controller)架构:在MVC架构中,控制器(Controller)充当中介者的角色,协调模型(Model)和视图(View)之间的交互。这样,模型和视图之间的耦合度降低,使得它们可以独立地进行修改和扩展。
- 网络游戏:在多人在线游戏中,游戏服务器可以充当中介者,管理玩家之间的通信和交互。这样,每个玩家只需要与游戏服务器通信,而不必知道其他玩家的详细信息。
- 电子商务平台:在电子商务平台中,买家和卖家需要进行交易。平台可以作为一个中介者,处理买家和卖家之间的通信和交易,包括订单处理、支付、退款等。这样,买家和卖家之间不需要直接进行通信,降低了交易的复杂性。
- 事件总线:在事件驱动的系统中,事件总线可以作为一个中介者,负责处理和分发事件。系统中的组件可以将事件发布到事件总线,而不必直接与其他组件通信。这样,组件之间的耦合度降低,系统变得更加模块化。
- 工作流引擎:在工作流引擎中,各个流程节点之间需要进行通信和协调。工作流引擎可以作为一个中介者,负责管理流程节点之间的通信,确保整个流程的正确执行。
这些案例展示了中介者模式在不同领域和场景中的应用。通过引入中介者,可以降低对象之间的耦合度,简化系统的设计和维护。
多人游戏案例
将展示如何在多人在线游戏中使用中介者模式。将创建一个简单的游戏,如五子棋,其中有两个玩家,分别为PlayerA和PlayerB。游戏服务器(GameServer)将充当中介者,负责处理玩家之间的消息传递。
定义中介者接口:
interface GameServerMediator {
void sendMessage(String message, Player player);
}
实现具体的中介者类:
class GameServer implements GameServerMediator {
private PlayerA playerA;
private PlayerB playerB;
public void setPlayerA(PlayerA playerA) {
this.playerA = playerA;
}
public void setPlayerB(PlayerB playerB) {
this.playerB = playerB;
}
@Override
public void sendMessage(String message, Player player) {
if (player == playerA) {
playerB.receiveMessage(message);
} else {
playerA.receiveMessage(message);
}
}
}
定义玩家抽象类:
abstract class Player {
protected GameServerMediator mediator;
public Player(GameServerMediator mediator) {
this.mediator = mediator;
}
public abstract void send(String message);
public abstract void receiveMessage(String message);
}
实现具体的玩家类:
class PlayerA extends Player {
public PlayerA(GameServerMediator mediator) {
super(mediator);
}
@Override
public void send(String message) {
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println("PlayerA收到消息: " + message);
}
}
class PlayerB extends Player {
public PlayerB(GameServerMediator mediator) {
super(mediator);
}
@Override
public void send(String message) {
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println("PlayerB收到消息: " + message);
}
}
在客户端代码中创建玩家对象并进行通信:
public class MultiplayerGameDemo {
public static void main(String[] args) {
GameServer gameServer = new GameServer();
PlayerA playerA = new PlayerA(gameServer);
PlayerB playerB = new PlayerB(gameServer);
gameServer.setPlayerA(playerA);
gameServer.setPlayerB(playerB);
playerA.send("你好,PlayerB!");
playerB.send("你好,PlayerA! 一起玩游戏吗?");
playerA.send("好的,我们开始吧!");
}
}
输出结果如下:
PlayerB收到消息: 你好,PlayerB!
PlayerA收到消息: 你好,PlayerA! 一起玩游戏吗?
PlayerB收到消息: 好的,我们开始吧!
在这个例子中创建了一个简单的多人在线游戏,其中GameServer充当中介者,负责管理PlayerA和PlayerB之间的通信。玩家对象不直接相互通信,而是通过GameServer进行消息传递。如果需要添加更多玩家,只需在GameServer中进行相应的修改即可,而无需修改现有的玩家类。这降低了系统的耦合度,提高了可扩展性和可维护性。
假设要在游戏中添加第三个玩家PlayerC。首先需要创建一个新的玩家类:
class PlayerC extends Player {
public PlayerC(GameServerMediator mediator) {
super(mediator);
}
@Override
public void send(String message) {
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println("PlayerC收到消息: " + message);
}
}
需要更新GameServer类以支持第三个玩家:
class GameServer implements GameServerMediator {
private PlayerA playerA;
private PlayerB playerB;
private PlayerC playerC;
// ...省略setPlayerA()和setPlayerB()方法...
public void setPlayerC(PlayerC playerC) {
this.playerC = playerC;
}
@Override
public void sendMessage(String message, Player player) {
if (player == playerA) {
playerB.receiveMessage(message);
playerC.receiveMessage(message);
} else if (player == playerB) {
playerA.receiveMessage(message);
playerC.receiveMessage(message);
} else {
playerA.receiveMessage(message);
playerB.receiveMessage(message);
}
}
}
在客户端代码中添加第三个玩家,并进行通信:
public class MultiplayerGameDemo {
public static void main(String[] args) {
GameServer gameServer = new GameServer();
PlayerA playerA = new PlayerA(gameServer);
PlayerB playerB = new PlayerB(gameServer);
PlayerC playerC = new PlayerC(gameServer);
gameServer.setPlayerA(playerA);
gameServer.setPlayerB(playerB);
gameServer.setPlayerC(playerC);
playerA.send("你好,PlayerB和PlayerC!");
playerB.send("你好,PlayerA和PlayerC! 一起玩游戏吗?");
playerC.send("我也加入!让我们开始吧!");
}
}
输出结果如下:
makefileCopy code
PlayerB收到消息: 你好,PlayerB和PlayerC!
PlayerC收到消息: 你好,PlayerB和PlayerC!
PlayerA收到消息: 你好,PlayerA和PlayerC! 一起玩游戏吗?
PlayerC收到消息: 你好,PlayerA和PlayerC! 一起玩游戏吗?
PlayerA收到消息: 我也加入!让我们开始吧!
PlayerB收到消息: 我也加入!让我们开始吧!
通过使用中介者模式,可以轻松地在游戏中添加更多玩家,而不需要修改现有玩家类。这使得游戏的设计更加灵活和可扩展。
机场调度案例
在机场调度系统的例子中,可以使用中介者模式来协调飞机、跑道和其他设施之间的交互。在这个例子中将创建一个简化的调度系统,其中有两个飞机和两个跑道。机场调度塔(AirportControlTower)将作为中介者,负责协调飞机和跑道之间的通信。
定义中介者接口:
interface ControlTowerMediator {
void requestRunway(Flight flight);
void releaseRunway(Flight flight);
}
实现具体的中介者类:
class AirportControlTower implements ControlTowerMediator {
private Runway runway1;
private Runway runway2;
public void setRunway1(Runway runway1) {
this.runway1 = runway1;
}
public void setRunway2(Runway runway2) {
this.runway2 = runway2;
}
@Override
public void requestRunway(Flight flight) {
if (!runway1.isOccupied()) {
runway1.assignFlight(flight);
} else if (!runway2.isOccupied()) {
runway2.assignFlight(flight);
} else {
System.out.println("所有跑道均被占用,请稍候再试。");
}
}
@Override
public void releaseRunway(Flight flight) {
if (runway1.getCurrentFlight() == flight) {
runway1.release();
} else if (runway2.getCurrentFlight() == flight) {
runway2.release();
}
}
}
定义跑道类:
class Runway {
private Flight currentFlight;
private String name;
public Runway(String name) {
this.name = name;
}
public void assignFlight(Flight flight) {
currentFlight = flight;
System.out.println("跑道" + name + "分配给航班" + flight.getFlightNumber());
}
public void release() {
System.out.println("跑道" + name + "释放,航班" + currentFlight.getFlightNumber() + "已离开。");
currentFlight = null;
}
public Flight getCurrentFlight() {
return currentFlight;
}
public boolean isOccupied() {
return currentFlight != null;
}
}
定义飞机类:
class Flight {
private String flightNumber;
private ControlTowerMediator mediator;
public Flight(String flightNumber, ControlTowerMediator mediator) {
this.flightNumber = flightNumber;
this.mediator = mediator;
}
public void requestRunway() {
mediator.requestRunway(this);
}
public void releaseRunway() {
mediator.releaseRunway(this);
}
public String getFlightNumber() {
return flightNumber;
}
}
在客户端代码中创建飞机和跑道对象,并模拟调度过程:
public class AirportControlSystemDemo {
public static void main(String[] args) {
AirportControlTower controlTower = new AirportControlTower();
Runway runway1 = new Runway("1");
Runway runway2 = new Runway("2");
controlTower.setRunway1(runway1);
controlTower.setRunway2(runway2);
Flight flight1 = new Flight("CA001", controlTower);
Flight flight2 = new Flight("MU002", controlTower);
Flight flight3 = new Flight("CZ003", controlTower);
// 航班1请求跑道
flight1.requestRunway();
// 航班2请求跑道
flight2.requestRunway();
// 航班3请求跑道,但所有跑道都被占用
flight3.requestRunway();
// 航班1释放跑道
flight1.releaseRunway();
// 航班3再次请求跑道,此时可以分配
flight3.requestRunway();
}
}
输出结果如下:
跑道1分配给航班CA001 跑道2分配给航班MU002 所有跑道均被占用,请稍候再试。 跑道1释放,航班CA001已离开。 跑道1分配给航班CZ003
在这个例子中创建了一个简化的机场调度系统,其中AirportControlTower充当中介者,负责管理飞机和跑道之间的交互。飞机和跑道对象之间没有直接通信,而是通过AirportControlTower进行调度。这使得系统的设计更加模块化,可以方便地添加更多飞机、跑道或其他设施,而无需修改现有的类。
上述示例展示了如何在机场调度系统中使用中介者模式。AirportControlTower作为中介者负责协调飞机和跑道之间的交互。当飞机需要请求或释放跑道时,它们会通过AirportControlTower进行操作。这样的设计降低了系统的耦合度,提高了可扩展性和可维护性。
中介模式 VS 观察者模式
中介者模式和观察者模式都是用于解决对象之间的通信问题,但它们的关注点和应用场景有所不同。下面对比一下它们之间的主要区别:
- 关注点:
- 中介者模式:关注的是如何减少对象之间的直接耦合,从而提高系统的可扩展性和可维护性。在中介者模式中,对象之间不直接通信,而是通过中介者对象进行交互。中介者对象负责处理其他对象之间的交互逻辑,使各个对象之间的依赖关系得到解耦。
- 观察者模式:关注的是如何在对象间实现一种一对多的依赖关系,使得当一个对象的状态发生改变时,其他依赖于它的对象都能得到通知并自动更新。观察者模式中的对象之间具有明确的被观察者(Subject)和观察者(Observer)角色。
- 应用场景:
- 中介者模式:适用于那些具有复杂对象交互关系的系统,例如界面组件之间的交互、协同工作的多个模块之间的通信等。通过引入中介者,可以降低系统的耦合度,使得各个对象之间更加独立,便于修改和扩展。
- 观察者模式:适用于那些需要在对象间实现一种一对多的依赖关系的场景,例如数据驱动的UI更新、事件驱动的系统等。观察者模式允许被观察者对象在其状态发生改变时通知所有关注它的观察者对象,实现了状态同步。
- 实现方式:
- 中介者模式:中介者模式中,对象之间通常通过注册到中介者,然后中介者负责协调它们之间的交互。这样,对象之间不需要直接引用其他对象,从而降低了耦合度。
- 观察者模式:观察者模式中,被观察者对象维护了一个观察者列表,当其状态发生改变时,会通知列表中的所有观察者。观察者对象通常通过实现一个特定的接口来实现对被观察者的关注。
中介者模式和观察者模式虽然都可以用于解决对象间的通信问题,但它们侧重的点和适用场景有所不同。中介者模式主要用于降低对象间的耦合度,而观察者模式主要用于实现一
虽然经典的实现方式没法彻底解耦观察者和被观察者,观察者需要注册到被观察者中,被观察者状态更新需要调用观察者的 update() 方法。但是,在跨进程的实现方式中,可以利用消息队列实现彻底解耦,观察者和被观察者都只需要跟消息队列交互,观察者完全不知道被观察者的存在,被观察者也完全不知道观察者的存在。
中介模式也是为了解耦对象之间的交互,所有的参与者都只与中介进行交互。而观察者模式中的消息队列,就有点类似中介模式中的“中介”,观察者模式的中观察者和被观察者,就有点类似中介模式中的“参与者”。那问题来了:中介模式和观察者模式的区别在哪里呢?什么时候选择使用中介模式?什么时候选择使用观察者模式呢?
在观察者模式中,尽管一个参与者既可以是观察者,同时也可以是被观察者,但是,大部分情况下,交互关系往往都是单向的,一个参与者要么是观察者,要么是被观察者,不会兼具两种身份。也就是说,在观察者模式的应用场景中,参与者之间的交互关系比较有条理。
而中介模式正好相反。只有当参与者之间的交互关系错综复杂,维护成本很高的时候,才考虑使用中介模式。毕竟,中介模式的应用会带来一定的副作用,前面也讲到,它有可能会产生大而复杂的上帝类。除此之外,如果一个参与者状态的改变,其他参与者执行的操作有一定先后顺序的要求,这个时候,中介模式就可以利用中介类,通过先后调用不同参与者的方法,来实现顺序的控制,而观察者模式是无法实现这样的顺序要求的。