模式定义:
先看看 中介 这个词的含义: 指在不同事务或同一事物内部对立两极(买卖,发送接收 等等)之间起居间联系作用的环节。对立的两极通过中介联成一体;
所以中介模式 就是 限制 对象间的直接交互,所有交互必须通过 中介对象 实现; 中介起到了居中联系,把 原本对象间 复杂的蛛网状的连接 改为了 星状结构;
生活中的例子:
- 房产中介; 一个中介 会有 好多个 客户 和 好多个房源信息; 客户把自己的要求 告诉中介,中介会 带你去看各种符合要求的房源; 然后开始 口若悬河......
- 航空塔楼; 给出需要降落飞机的降落时间 和跑道;
- 古代的媒婆; 男女双方的任何通信 都要通过 媒婆去处理...
代码中的例子:
- mvc模式中的 c(控制器) 就是中介, 用于控制 模型与视图的结合;
何时该使用此模式:
- 在 多个类间 存在 复杂网状的相互调用时,可以使用此模式;且可以让类 进行复用;
- 为了能在不同情景下复用一些基本行为,导致你需要被迫创建大量组件子类时,可使用中介者模式;
实现方式:
-
找到一组当前紧密耦合, 且提供其独立性能带来更大好处的类 (例如更易于维护或更方便复用)。
-
声明中介者接口并描述中介者和各种组件之间所需的交流接口。 在绝大多数情况下, 一个接收组件通知的方法就足够了。 如果你希望在不同情景下复用组件类, 那么该接口将非常重要。 只要组件使用通用接口与其中介者合作, 你就能将该组件与不同实现中的中介者进行连接。
-
实现具体中介者类。 该类可从自行保存其下所有组件的引用中受益。
-
组件必须保存对于中介者对象的引用。 该连接通常在组件的构造函数中建立, 该函数会将中介者对象作为参数传递。
-
修改组件代码, 使其可调用中介者的通知方法, 而非其他组件的方法。 然后将调用其他组件的代码抽取到中介者类中, 并在中介者接收到该组件通知时执行这些代码。
该模式关键的角色:
- 抽象中介者(Abs Mediator)角色: 实现 保存 组件对象的引用; 定义子类的公共的方法;
- 具体中介者(Concrete Mediator)角色:实现具体的组件间调用方法;
- 抽象组件类(Abs Component)角色:定义子类公共的方法; 保存 中介者对象的引用 方法(可以不用);
- 具体组件类(Concrete Component)角色: 实现组件的具体功能方法;
该模式的主要优缺点如下:
优点:
- 符合单一职责原则: 将多个组件间的通信 抽取到 一个类中实现; 组件类负责 实现自己的功能; 中介类负责 组件请求的处理(调用其他组件配合发送组合完成任务)
- 部分符合开闭原则: 在 新增一个组件时,其他组件没有影响;中介类 只需增加相应的 处理方法即可; 但是在 组件修改时,中介类中 也要进行修改;
- 使 组件间的直接耦合 转换到了 中介类中,方便统一的管理;
- 可以很方便的复用各个组件;
缺点:
- 很明显, 组件间的耦合关系 转移到了 中介类中,变为了 中介 和 组件间的耦合,如果组件过多,彼此关系过于复杂 则会导致 中介类 的功能繁多,难以维护;(可以使用多个中介类,减少单一中介类的复杂度)
和 其他模式 的 比较:
与外观模式的比较: 虽然两者都是 组织类之间的操作;但是 外观模式是把内部复杂的类(系统)进行封装,方便客户端调用,其本身不提供额外的功能,子系统中的对象可以直接交流; 而 中介者模式 将组件间的沟通 集中化;避免直接交流;
示例代码部分:
如下示例代码有2种实现方式
第一种: 客户端代码通过 各个组件进行调用; 组件中需要保存中介者类的引用;
# -*- coding: utf-8 -*-
"""
(C) rgc
All rights reserved
create time '2021/1/4 09:06'
Usage:
中介者模式 使用示例
家里的 灯光,音响,空调 的调用关系
# Jack在家时的 调用关系
* 灯亮时,需要空调打开
* 音响播放音乐时,需要灯亮,空调打开
* 空调定时后,需要灯定时关闭,音响立刻关闭
# Cherry在家时的 调用关系
* 灯亮时,音响,空调都打开
* 音响播放音乐时,需要灯亮,空调关闭
* 空调定时后,需要灯立刻关闭,音响立刻关闭
此示例 客户端代码通过 各个组件进行调用
"""
from abc import ABC, abstractmethod
class Component(ABC):
"""
组件角色
"""
def __init__(self, mediator=None):
"""
初始化
:param mediator:
"""
self._mediator = mediator
@property
def mediator(self):
"""
获取属性值
:return:
"""
return self._mediator
@mediator.setter
def mediator(self, mediator):
"""
设置 属性值
:param mediator:
:return:
"""
self._mediator = mediator
@abstractmethod
def turn_on(self):
"""
打开电器
:return:
"""
pass
@abstractmethod
def turn_off(self):
"""
关闭电器
:return:
"""
pass
def crontab(self):
"""
定时电器
:return:
"""
pass
class Light(Component):
"""
具体组件角色
多功能灯
"""
def turn_on(self):
"""
客户调用此方法 开灯
:return:
"""
# 通知中介者操作
self.mediator.turn_on_light()
def _turn_on(self):
"""
对象内部调用 实际开灯
:return:
"""
print('灯光打开')
def turn_off(self):
"""
客户调用此方法 关灯
:return:
"""
print('灯光关闭')
def crontab(self):
"""
定时关灯
:return:
"""
print('定时关灯设置完成')
class SoundBox(Component):
"""
具体组件角色
音响
"""
def turn_on(self):
"""
客户调用此方法 打开音响
:return:
"""
# 通知中介者 操作
self.mediator.turn_on_sound_box()
def _turn_on(self):
"""
对象内部调用 实际 音响打开
:return:
"""
print('音响打开')
def turn_off(self):
"""
客户调用此方法 关闭音响
:return:
"""
print('音响关闭')
class AirConditioner(Component):
"""
具体组件角色
空调
"""
def turn_on(self):
"""
客户调用此方法 开空调
:return:
"""
print('空调打开')
def turn_off(self):
"""
客户调用此方法 开空调
:return:
"""
print('空调关闭')
def crontab(self):
"""
空调定时
:return:
"""
# 通知中介去 操作
self.mediator.crontab_air_conditioner()
def _crontab(self):
"""
空调定时
:return:
"""
print('空调定时设置完成')
class AbsMediator(ABC):
"""
抽象中介者角色
"""
def __init__(self, light: Light, sound_box: SoundBox, air_conditioner: AirConditioner):
"""
初始化
"""
self.light = light
self.sound_box = sound_box
self.air_conditioner = air_conditioner
for item in [light_obj, sound_box, air_conditioner]:
item.mediator = self
class JackMediator(AbsMediator):
"""
具体中介者角色
此处为 Jack在家的中介者
"""
def turn_on_light(self):
"""
灯亮时,需要空调打开
:return:
"""
self.light._turn_on()
self.air_conditioner.turn_on()
def turn_on_sound_box(self):
"""
音响播放音乐时,需要灯亮,空调打开
:return:
"""
self.sound_box._turn_on()
self.light._turn_on()
self.air_conditioner.turn_on()
def crontab_air_conditioner(self):
"""
空调定时后,需要灯定时关闭,音响立刻关闭
:return:
"""
self.air_conditioner._crontab()
self.light.crontab()
self.sound_box.turn_off()
class CherryMediator(AbsMediator):
"""
具体中介者角色
此处为 Cherry在家的中介者
"""
def turn_on_light(self):
"""
灯亮时,音响,空调都打开
:return:
"""
self.light._turn_on()
self.sound_box._turn_on()
self.air_conditioner.turn_on()
def turn_on_sound_box(self):
"""
音响播放音乐时,需要灯亮,空调关闭
:return:
"""
self.sound_box._turn_on()
self.light._turn_on()
self.air_conditioner.turn_off()
def crontab_air_conditioner(self):
"""
空调定时后,需要灯立刻关闭,音响立刻关闭
:return:
"""
self.air_conditioner._crontab()
self.light.turn_off()
self.sound_box.turn_off()
if __name__ == "__main__":
# 组件角色实例化
light_obj = Light()
sound_box_obj = SoundBox()
air_conditioner_obj = AirConditioner()
# Jack在家中介者实例化
JackMediator(light_obj, sound_box_obj, air_conditioner_obj)
print('Jack在家进行操作', '-' * 10)
print('灯亮时,需要空调打开')
light_obj.turn_on()
print('音响播放音乐时,需要灯亮,空调打开')
sound_box_obj.turn_on()
print('空调定时后,需要灯定时关闭,音响立刻关闭')
air_conditioner_obj.crontab()
print('*' * 20)
# Cherry在家中介者实例化
CherryMediator(light_obj, sound_box_obj, air_conditioner_obj)
print('Cherry在家进行操作', '-' * 10)
print('灯亮时,音响,空调都打开')
light_obj.turn_on()
print('音响播放音乐时,需要灯亮,空调关闭')
sound_box_obj.turn_on()
print('空调定时后,需要灯立刻关闭,音响立刻关闭')
air_conditioner_obj.crontab()
第二种: 客户端代码通过 中介者进行调用; 这时 组件类 不需要 保存中介者类的引用; 这样减少代码量
# -*- coding: utf-8 -*-
"""
(C) rgc
All rights reserved
create time '2021/1/4 09:06'
Usage:
中介者模式 使用示例
家里的 灯光,音响,空调 的调用关系
# Jack在家时的 调用关系
* 灯亮时,需要空调打开
* 音响播放音乐时,需要灯亮,空调打开
* 空调定时后,需要灯定时关闭,音响立刻关闭
# Cherry在家时的 调用关系
* 灯亮时,音响,空调都打开
* 音响播放音乐时,需要灯亮,空调关闭
* 空调定时后,需要灯立刻关闭,音响立刻关闭
此示例 客户端代码通过 中介者进行调用
"""
from abc import ABC, abstractmethod
class Component(ABC):
"""
组件角色
"""
@abstractmethod
def turn_on(self):
"""
打开电器
:return:
"""
pass
@abstractmethod
def turn_off(self):
"""
关闭电器
:return:
"""
pass
def crontab(self):
"""
定时电器
:return:
"""
pass
class Light(Component):
"""
具体组件角色
多功能灯
"""
def turn_on(self):
"""
对象内部调用 实际开灯
:return:
"""
print('灯光打开')
def turn_off(self):
"""
客户调用此方法 关灯
:return:
"""
print('灯光关闭')
def crontab(self):
"""
定时关灯
:return:
"""
print('定时关灯设置完成')
class SoundBox(Component):
"""
具体组件角色
音响
"""
def turn_on(self):
"""
对象内部调用 实际 音响打开
:return:
"""
print('音响打开')
def turn_off(self):
"""
客户调用此方法 关闭音响
:return:
"""
print('音响关闭')
class AirConditioner(Component):
"""
具体组件角色
空调
"""
def turn_on(self):
"""
客户调用此方法 开空调
:return:
"""
print('空调打开')
def turn_off(self):
"""
客户调用此方法 开空调
:return:
"""
print('空调关闭')
def crontab(self):
"""
空调定时
:return:
"""
print('空调定时设置完成')
class AbsMediator(ABC):
"""
抽象中介者角色
"""
def __init__(self, light: Light, sound_box: SoundBox, air_conditioner: AirConditioner):
"""
初始化
"""
self.light = light
self.sound_box = sound_box
self.air_conditioner = air_conditioner
class JackMediator(AbsMediator):
"""
具体中介者角色
此处为 Jack在家的中介者
"""
def turn_on_light(self):
"""
灯亮时,需要空调打开
:return:
"""
self.light.turn_on()
self.air_conditioner.turn_on()
def turn_on_sound_box(self):
"""
音响播放音乐时,需要灯亮,空调打开
:return:
"""
self.sound_box.turn_on()
self.light.turn_on()
self.air_conditioner.turn_on()
def crontab_air_conditioner(self):
"""
空调定时后,需要灯定时关闭,音响立刻关闭
:return:
"""
self.air_conditioner.crontab()
self.light.crontab()
self.sound_box.turn_off()
class CherryMediator(AbsMediator):
"""
具体中介者角色
此处为 Cherry在家的中介者
"""
def turn_on_light(self):
"""
灯亮时,音响,空调都打开
:return:
"""
self.light.turn_on()
self.sound_box.turn_on()
self.air_conditioner.turn_on()
def turn_on_sound_box(self):
"""
音响播放音乐时,需要灯亮,空调关闭
:return:
"""
self.sound_box.turn_on()
self.light.turn_on()
self.air_conditioner.turn_off()
def crontab_air_conditioner(self):
"""
空调定时后,需要灯立刻关闭,音响立刻关闭
:return:
"""
self.air_conditioner.crontab()
self.light.turn_off()
self.sound_box.turn_off()
if __name__ == "__main__":
# 组件角色实例化
light_obj = Light()
sound_box_obj = SoundBox()
air_conditioner_obj = AirConditioner()
# Jack在家中介者实例化
jack_mediator_obj = JackMediator(light_obj, sound_box_obj, air_conditioner_obj)
print('Jack在家进行操作', '-' * 10)
print('灯亮时,需要空调打开')
jack_mediator_obj.turn_on_light()
print('音响播放音乐时,需要灯亮,空调打开')
jack_mediator_obj.turn_on_sound_box()
print('空调定时后,需要灯定时关闭,音响立刻关闭')
jack_mediator_obj.crontab_air_conditioner()
print('*' * 20)
# Cherry在家中介者实例化
cherry_mediator_obj = CherryMediator(light_obj, sound_box_obj, air_conditioner_obj)
print('Cherry在家进行操作', '-' * 10)
print('灯亮时,音响,空调都打开')
cherry_mediator_obj.turn_on_light()
print('音响播放音乐时,需要灯亮,空调关闭')
cherry_mediator_obj.turn_on_sound_box()
print('空调定时后,需要灯立刻关闭,音响立刻关闭')
cherry_mediator_obj.crontab_air_conditioner()
运行结果:
总结:
中介者模式 将原本组件间直接相互调用的方式改为 由中介者类 进行统一管理调用; 但是在功能复杂后,会导致 中介者类 功能过于复杂,难以维护;这时 可以将 中介者类 拆分成N个中介者类,减少逻辑判断代码(if else ..);从而消灭这种问题;
网上看了 中介者模式的 实际代码实现,无论python还是java; 存在 如上示例中的2种实现方式;要么 客户端直接调用各个组件;要么 客户端调用 中介者方法; 个人推荐 客户端调用中介者 方法,中介者自己去完全的管理组件;
因为这样 可以减少代码量; 组件对象 不用保存 中介者对象的引用;
相关链接:
https://refactoringguru.cn/design-patterns/mediator
http://c.biancheng.net/view/1393.html