设计模式 ——— 中介者模式

作者: tomas家的小拨浪鼓 | 来源:发表于2017-09-26 01:48 被阅读0次

    意图

    用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

    中介者模式的本质:封装交互。

    适用性

    • 如果一组对象之间的通信方式比较复杂,导致相互依赖、结构混乱,可以采用中介者模式,把这些对象相互的交互管理起来,各个对象都只需要和中介者交互,从而使得各个对象松散耦合,结构也更清晰易懂。
    • 如果一个对象引用很多的对象,并直接跟这些对象交互,导致难以复用该对象。可以采用中介者模式,把这个对象跟其它对象的交互封装到中介者对象里面,这个对象就只需要和中介者对象交互就可以了。

    结构

    中介者模式结构图
    • Mediator(中介者接口)
      在里面定义各个同事之间交互需要的方法,可以是公共的通讯方法,比如changed方法,大家都用,也可以是小范围的交互方法。
    • ConcreteMediator(具体中介者类)
      它需要了解并维护各个同事对象,并负责具体的协调各同事对象的交互关系。
    • Colleague(同事类)
      通常实现成为抽象类,主要负责约束同事对象的类型,并实现一些具体同事类之间的公共功能,比如:每个具体同事类都应该知道中介者对象,也就是具体同事类都会持有中介者对象,就可以定义到这个类里面。
    • ConcreteColleague(具体同事类)
      具体的同事类,实现自己的业务。每一个同事类都知道它的中介者对象。每一个同事对象在需与其他的同事通信的时候,与它的中介者通信。

    场景解说

    下面我们通过一个场景来让大家更容易对中介者模式有一个直观的认识。
    场景:
    感谢未来屋公司的这群好家伙,Bob拥有一个Java版本的自动屋,这可以让他的生活变得更便利。当Bob点击了打盹按钮,他的闹钟就会告诉咖啡壶开始煮咖啡。尽管生活对他来说是如此惬意,但他(以及其他的客户)总是不断地提出许多新的要求:周末不要供应咖啡......在洗澡前将喷头关闭15分钟......在丢垃圾的日子里将闹钟时刻提前......



    问题:想要持续地追踪每个对象的每个规则,以及众多对象之间彼此错综复杂的关系,实在不容易。
    解决方案:使用中介者模式
    仔细分析上面的问题,根本原因就在于多个对象需要相互交互,从而导致对象之间紧密耦合,这就不利于对象的修改和维护。
    在这个系统中加入一个中介者,一切都变得简单了。

    • 每个对象都会在自己的状态改变时,告诉中介者
    • 每个对象都会对中介者所发出的请求作出回应。

    在没有中介者的情况下,所有的对象都需要认识其他对象......也就是说,对象之间是紧耦合的。有了中介者之后,对象之间彻底解耦。
    中介者内包含了整个系统的控制逻辑。当某装置需要一个新的规则时,或者是一个新的装置被加入系统内,其所有需要用到的逻辑也都被加进了中介者内。



    对中介者模式实现的深入探讨

    • 忽略抽象的Mediator类
      有没有使用Mediator接口的必要,那就取决于是否会提供多个不同的中介者实现。如果中介者实现只有一个的话,而且预计中也没有需要扩展的要求,那么就可以不定义Mediator接口,让各个同事对象直接使用中介者实现对象;如果中介者实现不只一个,或者预计中有扩展的要求,那么就需要定义Mediator接口,让各个同事对象来面向中介者接口编程,而无需关心具体的中介者实现。
    • Colleague ———— Mediator 通信
      Colleague 与 Mediator 之间通信有两种实现方式:
      ① 第一种实现方式是:使用观察者模式,将Mediator实现为一个Observer,各Colleague作为Subject,一旦其状态改变就发送通知给Mediator。Mediator作出的响应是将状态改变的结果传播给其他的Colleague。
      ② 第二种实现方式是:在Mediator中定义一个特殊的通知接口,各Colleague在通信时直接调用该接口。

    优缺点

    优点

    ① 松散耦合
    中介者模式通过把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互不依赖。这样一来,同事对象就可以独立的变化和复用,而不再像以前那样“牵一发而动全身”了。

    ② 集中控制交互
    多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时候,只需要修改中介者对象就可以了,而各个同事类不需要做修改。这使得你将注意力从对象各自本身的行为转移到它们之间的交互上来,这有助于弄清楚一个系统中的对象是如何交互的。

    ③ 多对多变成一对多
    没有使用中介者模式的时候,同事对象之间的关系通常是多对多的,引入中介者对象过后,中介者对象和同事对象的关系通常变成了双向的一对多,这会让对象的关系更容易理解和实现。

    缺点

    ① 过度集中化
    中介者模式的一个潜在缺点是,如果同事对象的交互非常多,而且比较复杂,当这些复杂性全部集中到中介者的时候,会导致中介者对象变得十分的复杂,而且难于管理和维护。

    广义中介者

    仔细查看中介者的结构、定义和示例,会发现几个问题,使得中介者模式在实际使用的时候,变得繁琐或困难。
    Q:是否有必要为同事对象定义一个公共的父类?
    大家都知道,Java是单继承的,为了使用中介者模式,就让这些同事对象继承一个父类,这是很不好的;再说了,这个父类目前也没有什么特别的公共功能,也就是说继承它也得不到多少好处。
    A:在实际开发中,很多相互交互的对象本身是没有公共父类的,强行加上一个父类,会让这些对象实现起来特别别扭。

    Q:同事类有必要持有中介者对象吗?
    同事类需要知道中介者对象,以便当它们发生改变的时候,能够通知中介者对象,但是,是否需要作为属性,并通过构造方法传入,这么强的依赖关系呢?
    A:也可以有简单的方式去通知中介对象,比如把中介对象做成单例,直接在同事类的方法里面去调用中介者对象。

    Q:是否需要中介者接口?
    A:在实际开发中,很常见的情况是不需要中介者接口的,而且中介者对象也不需要创建很多个实例,因为中介者是用来封装和处理同事对象的关系的,它一般是没有状态需要维护的,因此中介者通常可以实现成单例。

    Q:其四:中介者对象是否需要持有所有的同事?
    A:虽说中介者对象需要知道所有的同事类,这样中介者才能与它们交互。但是是否需要做为属性这么强烈的依赖关系,而且中介者对象在不同的关系维护上,可能会需要不同的同事对象的实例,因此可以在中介者处理的方法里面去创建、或者获取、或者从参数传入需要的同事对象。

    Q:中介者对象只是提供一个公共的方法,来接受同事对象的通知吗?
    A:在实际开发中,通常会提供具体的业务通知方法,这样就不用再去判断到底是什么对象,具体是什么业务了。

    实际开发中我们使用更多的是中介者模式的变种:
    只要是实现封装对象之间的交互功能,就可以应用上中介者模式,而不必过于拘泥于中介者模式本身的结构。标准的中介者模式限制很多,导致能完全按照标准使用中介者模式的地方并不是很多,而且多集中在界面实现上。只要本质不变,稍稍变形一下,简化一下,或许能更好的使用中介者模式。

    相关模式

    中介者模式和外观模式

    这两个模式有相似的地方,也存在很大的不同。

    外观模式多用来封装一个子系统内部的多个模块,目的是向子系统外部提供简单易用的接口,也就是说外观模式封装的是子系统外部和子系统内部模块间的交互;而中介者模式是提供多个平等的同事对象之间交互关系的封装,一般是用在内部实现上。

    另外,外观模式是实现单向的交互,是从子系统外部来调用子系统内部,不会反着来,而中介者模式实现的是内部多个模块间多向的交互。

    中介者模式和观察者模式

    这两个模式可以组合使用。
    中介者模式可以组合使用观察者模式,来实现当同事对象发生改变的时候,通知中介对象,让中介对象去进行与其它相关对象的交互。

    参考

    《Head First 设计模式》
    《设计模式:可复用面向对象软件的基础》
    《研磨设计模式》

    相关文章

      网友评论

        本文标题:设计模式 ——— 中介者模式

        本文链接:https://www.haomeiwen.com/subject/wwmjextx.html