中介者模式(Mediator)及代码实现

模式定义:

先看看 中介 这个词的含义: 指在不同事务或同一事物内部对立两极(买卖,发送接收 等等)之间起居间联系作用的环节。对立的两极通过中介联成一体;

所以中介模式 就是  限制 对象间的直接交互,所有交互必须通过 中介对象 实现; 中介起到了居中联系,把 原本对象间 复杂的蛛网状的连接 改为了 星状结构;

 

生活中的例子:

  • 房产中介; 一个中介 会有 好多个 客户 和 好多个房源信息; 客户把自己的要求 告诉中介,中介会 带你去看各种符合要求的房源; 然后开始 口若悬河......
  • 航空塔楼; 给出需要降落飞机的降落时间 和跑道; 
  • 古代的媒婆; 男女双方的任何通信 都要通过 媒婆去处理...

 

代码中的例子:

  • mvc模式中的 c(控制器) 就是中介, 用于控制 模型与视图的结合;

 

何时该使用此模式:

  • 在 多个类间 存在 复杂网状的相互调用时,可以使用此模式;且可以让类 进行复用;
  • 为了能在不同情景下复用一些基本行为,导致你需要被迫创建大量组件子类时,可使用中介者模式;

 

实现方式:

  1. 找到一组当前紧密耦合, 且提供其独立性能带来更大好处的类 (例如更易于维护或更方便复用)。

  2. 声明中介者接口并描述中介者和各种组件之间所需的交流接口。 在绝大多数情况下, 一个接收组件通知的方法就足够了。 如果你希望在不同情景下复用组件类, 那么该接口将非常重要。 只要组件使用通用接口与其中介者合作, 你就能将该组件与不同实现中的中介者进行连接。

  3. 实现具体中介者类。 该类可从自行保存其下所有组件的引用中受益。

  4. 组件必须保存对于中介者对象的引用。 该连接通常在组件的构造函数中建立, 该函数会将中介者对象作为参数传递。

  5. 修改组件代码, 使其可调用中介者的通知方法, 而非其他组件的方法。 然后将调用其他组件的代码抽取到中介者类中, 并在中介者接收到该组件通知时执行这些代码。

 

该模式关键的角色:

  • 抽象中介者(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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值