中介者模式(Mediator Pattern): 避免对象间错综复杂的相互引用

版权声明:本文为博主学习笔记, 注明来源情况下随意转载 https://blog.csdn.net/lengxiao1993/article/details/73608816
  1. 参考书籍: 《Design Patterns: Elements of Reusable Object-Oriented Software》

设计模式用前须知

大部分程序员编写的程序可以分为三类(应用程序、工具包 、框架),使用设计模式的目的是提高代码的可复用性和可扩展性(灵活性), 但是设计模式在这三类软件中所发挥的效果是不一样的。

很多有经验的程序员会得出“使用了设计模式,反而降低了代码的可读性,增加了复杂度”的结论, 并把这种问题总结为过度设计。 这种总结其实有失偏颇, 因为设计模式所提供的灵活性本身就是有代价的。 很多程序员所编写的商业应用程序, 其问题领域其实相对固定,对代码的灵活性和重用性要求并不高(相对于与工具包和框架而言), 所以应用程序的编写者往往在不了解设计模式的情况下,也可以很好地解决一些灵活性需求和扩展性需求。

具体来说使用设计模式的必要性的程度是逐级递增的:应用程序(Application) < 工具包/类库(ToolKit/Library) < 框架(Framework)

中介者模式(Vistor Pattern)

  • 设计意图

    • GoF: 定义一个对象用于封装一系列对象之间的交互。 中介者模式通过避免对象间的直接引用促进了松耦合, 从而使得你可以独立地修改多个对象的交互方式。
  • GoF 举例:

    • 面向对象的设计鼓励奖不同的行为分散到不同的对象中。但是这种分散可能会产生一种对象之间存在大量的相互引用的结构。 在最坏的情况下, 所有需要交互的对象都会以笛卡尔积的方式持有其他所有对象的引用。
    • 虽然把一个系统划分为多个对象通常会增强可重用性, 但是他们之间复杂交互有可能反过来减少可重用性。 大量的交互使得一个对象不能独立工作, 因此, 尽管讲一个系统划分为了多个对象, 但他们可能依旧作为一个盘根错节的庞然大物难以拆分。 在这种情况下, 想对这个系统的行为作出一些重大的改变时很困难的, 尽管这个系统的行为已经分散到众多对象中分别实现。
    • 考虑一个图像界面中,对话框的实现。 一个对话框使用一个 window 来展示一系列图形组件 : 按钮 (buttons), 菜单(menus) , 输入框(entry fileds)

    这里写图片描述

  • Tips: 上图是一个字体选择的对话框, The quick brown fox jumps over the lazy dog 一句包含26个英文字母的最短句子,以便从事新闻通讯或版面设计工作的用户在设计版面的过程中,能看到整个版面填满26种字母时的效果

通常, 界面组件之间存在着相互的依赖。 以上图为例:

  • 当输入框内容为空时, 确认按钮需要被禁用。
  • 在列表中选择一个条目需要使得输入框中的对应内容也跟着改变。 相反, 在输入框中输入文字以后, 选择列表中需要自动去匹配选中对应的选项。
  • 当输入框中输入了完整的,可以与选项列表中完全匹配的文字后, 确认按钮需要被启用。

不同的对话框中, 界面组件的依赖是不一样的, 尽管他们包含的组件有可能一样, 但组件之间不同的联动关系可能会使得一个已经定义好的对话框没有办法通过较小的改动得到重用。当然, 你可以定义一个类, 封装了各种组件, 通过继承该类, 仅仅重写组件交互部分的代码来实现不同对话框的定制, 但如果需要交互的类很多时, 这种定制会变得非常麻烦。

  • 解决方案

    • 中介者模式通过将复杂的交互行为封装到一个 mediator 对象中来避免上述提到的问题。 一个 mediator 负责控制和协调一组对象之间的交互。 mediator 作为一个中间人, 避免一组需要交互的对象相互直接持有各自的引用, 这些对象仅仅了解 mediator , 从而减少了对象之间的交叉引用数量。
  • 以上文中的对话框为例, 可以定义一个 FontDialogDirector 作为不同界面组件的 mediator 。 FontDialogDirector 负责协调不同界面组件的交互。 它的角色就像是一个组件间通信的交换机一样。

这里写图片描述

  • 下图以ListBox 中的选项的改变为例,说明 FontDialogDirector 作为 mediator 如何协调不同组件之间的交互。

这里写图片描述

  1. aListBox 选项内容发生变化时调用 aFontDialogDirecotor 的 widgetChanged() 方法
  2. fontDialogDirector 调用 aListBox 的 getSelection() 方法获取 aListBox 被选中的内容
  3. fontDialogDirector 调用 anEntryFiled 的 setText() 方法, 将刚才获得的内容设置到输入框中。

从以上的交互过程和对象引用结构上,我们可以发现, director 扮演了交互协调者的角色, listBox, entryFiled 之间并没有直接相互调用, 他们仅仅持有 director 的引用。 由于这些交互都被集中到了 director 一个类中 , 如需修改组件的交互方式, 可以通过替换或扩展 director 类来实现。 下图为一个完整的类层级结构展示

这里写图片描述

  • 图例说明
    • 图片中的空心三角箭头,代表着继承(extends)或实现(Implement)关系, 由继承者/实现者 指向 被继承者/被继承者。
    • 图片中的实心三角箭头且箭头末尾没有圆圈的, 代表着单一的引用关系, 但是被引用的对象也有可能被其他对象引用。
    • 图片中的实心三角箭头且箭头末尾有圆圈的, 代表着一对多的引用关系。
    • 图片中的末端有圆圈的虚线是一个对方法体内容用伪代码说明的关系

应用场景

在如下情况考虑使用中介者模式

  • 一系列的对象相互之间需要进行定义明确, 却较为复杂的交互。 这些需要交互的对象如果相互间直接引用会产生难于理解的复杂结构。
  • 希望重用一个对象时发现很困难, 因为该对象涉及到了和其他多个对象的交互。
  • 一个分散于多个类的行为需要支持定制, 但却不希望通过创建大量的子类。

中介模式结构

这里写图片描述

这里写图片描述

中介模式总结

中介模式的好处如下

  • 通过引入一个 mediator 对象来管理多个需要交互的对象, 避免他们之间直接引用彼此, 产生混乱的引用关系。
  • 由于多个对象的交互方式集中在了 mediator 实现, 当需要改变多个组件的交互方式时, 扩展 mediator 的实现即可。

猜你喜欢

转载自blog.csdn.net/lengxiao1993/article/details/73608816