协调多个对象之间的交互——中介者模式(一)

腾讯公司推出的QQ作为一款免费的即时聊天软件深受广大用户的喜爱,它已经成为很多人学习、工作和生活的一部分(不要告诉我你没有QQ哦)。在QQ聊天中,一般有两种聊天方式:第一种是用户与用户直接聊天,第二种是通过QQ群聊天,如图20-1所示。如果我们使用图20-1(A)所示方式,一个用户如果要与别的用户聊天或发送文件,通常需要加其他用户为好友,用户与用户之间存在多对多的联系,这将导致系统中用户之间的关系非常复杂,一个用户如果要将相同的信息或文件发送给其他所有用户,必须一个一个的发送,于是QQ群产生了,如图20-1(B)所示,如果使用QQ群,一个用户就可以向多个用户发送相同的信息和文件而无须一一进行发送,只需要将信息或文件发送到群中或作为群共享即可,群的作用就是将发送者所发送的信息和文件转发给每一个接收者用户。通过引入群的机制,将极大减少系统中用户之间的两两通信,用户与用户之间的联系可以通过群来实现。

协调多个对象之间的交互——中介者模式(一) - 图1

图20-1 QQ聊天示意图

在有些软件中,某些类/对象之间的相互调用关系错综复杂,类似QQ用户之间的关系,此时,我们特别需要一个类似“QQ群”一样的中间类来协调这些类/对象之间的复杂关系,以降低系统的耦合度。有一个设计模式正为此而诞生,它就是本章将要介绍的中介者模式。

20.1 客户信息管理窗口的初始设计

Sunny软件公司欲开发一套CRM系统,其中包含一个客户信息管理模块,所设计的“客户信息管理窗口”界面效果图如图20-2所示:

协调多个对象之间的交互——中介者模式(一) - 图2

图20-2 “客户信息管理窗口”界面图

Sunny公司开发人员通过分析发现,在图20-2中,界面组件之间存在较为复杂的交互关系:如果删除一个客户,要在客户列表(List)中删掉对应的项,客户选择组合框(ComboBox)中客户名称也将减少一个;如果增加一个客户信息,客户列表中需增加一个客户,且组合框中也将增加一项。

如果实现界面组件之间的交互是Sunny公司开发人员必须面对的一个问题?

Sunny公司开发人员对组件之间的交互关系进行了分析,结果如下:

(1) 当用户单击“增加”按钮、“删除”按钮、“修改”按钮或“查询”按钮时,界面左侧的“客户选择组合框”、“客户列表”以及界面中的文本框将产生响应。

(2) 当用户通过“客户选择组合框”选中某个客户姓名时,“客户列表”和文本框将产生响应。

(3) 当用户通过“客户列表”选中某个客户姓名时,“客户选择组合框”和文本框将产生响应。

于是,Sunny公司开发人员根据组件之间的交互关系绘制了如图20-3所示初始类图:

协调多个对象之间的交互——中介者模式(一) - 图3

图20-3 “客户信息管理窗口”原始类图

与类图20-3所对应的框架代码片段如下:

  1. //按钮类
  2. class Button {
  3. private List list;
  4. private ComboBox cb;
  5. private TextBox tb;
  6. ......
  7. //界面组件的交互
  8. public void change() {
  9. list.update();
  10. cb.update();
  11. tb.update();
  12. }
  13. public void update() {
  14. ......
  15. }
  16. ......
  17. }
  18. //列表框类
  19. class List {
  20. private ComboBox cb;
  21. private TextBox tb;
  22. ......
  23. //界面组件的交互
  24. public void change() {
  25. cb.update();
  26. tb.update();
  27. }
  28. public void update() {
  29. ......
  30. }
  31. ......
  32. }
  33. //组合框类
  34. class ComboBox {
  35. private List list;
  36. private TextBox tb;
  37. ......
  38. //界面组件的交互
  39. public void change() {
  40. list.update();
  41. tb.update();
  42. }
  43. public void update() {
  44. ......
  45. }
  46. ......
  47. }
  48. //文本框类
  49. class TextBox {
  50. public void update() {
  51. ......
  52. }
  53. ......
  54. }

分析图20-3所示初始结构图和上述代码,我们不难发现该设计方案存在如下问题:

(1) 系统结构复杂且耦合度高:每一个界面组件都与多个其他组件之间产生相互关联和调用,若一个界面组件对象发生变化,需要跟踪与之有关联的其他所有组件并进行处理,系统组件之间呈现一种较为复杂的网状结构,组件之间的耦合度高。

(2) 组件的可重用性差:由于每一个组件和其他组件之间都具有很强的关联,若没有其他组件的支持,一个组件很难被另一个系统或模块重用,这些组件表现出来更像一个不可分割的整体,而在实际使用时,我们往往需要每一个组件都能够单独重用,而不是重用一个由多个组件组成的复杂结构。

(3) 系统的可扩展性差:如果在上述系统中增加一个新的组件类,则必须修改与之交互的其他组件类的源代码,将导致多个类的源代码需要修改,同样,如果要删除一个组件也存在类似的问题,这违反了“开闭原则”,可扩展性和灵活性欠佳。

由于存在上述问题,Sunny公司开发人员不得不对原有系统进行重构,那如何重构呢?大家想到了“迪米特法则”,引入一个“第三者”来降低现有系统中类之间的耦合度。由这个“第三者”来封装并协调原有组件两两之间复杂的引用关系,使之成为一个松耦合的系统,这个“第三者”又称为“中介者”,中介者模式因此而得名。下面让我们正式进入中介者模式的学习,学会如何使用中介者类来协调多个类/对象之间的交互,以达到降低系统耦合度的目的。