【设计模式】行为型模式其五: 中介者模式

545 阅读7分钟

中介者模式

什么是中介者模式

中介者模式是一个对象结构型模式。它提供了一种方式来降低多个对象之间的通信复杂性。

image.png

QQ聊天的两种方式:

  • 用户与用户直接聊天,用户与用户之间存在多对多的联系,这将导致系统中用户之间的关系非常复杂,一个用户如果要将相同的信息或文件发送给其他所有用户,必须一个一个地发送
  • 通过QQ群聊天,用户只需要将信息或文件发送到群中或上传为群共享文件即可,群的作用就是将发送者所发送的信息和文件转发给每一个接收者,将极大地减少系统中用户之间的两两通信

中介者就是很多对象之间的媒介,起到收消息与转发的作用。

比如我们的软件开发,多个对象之间相互联系,最终成为一个耦合度很高的系统,这对系统的修改是很不利的。

星型结构:中介者模式将系统的网状结构变成以中介者为中心的星型结构,同事对象不再直接与另一个对象联系,它通过中介者对象与另一个对象发生相互作用。系统的结构不会因为新对象的引入带来大量的修改工作

image.png

中介者模式定义

定义一个对象来封装一系列对象的交互。

中介者模式使各对象之间不需要显式地相互引用,从而使其耦合松散,而且让你可以独立地改变它们之间的交互。

  • 又称为调停者模式
  • 在中介者模式中,通过引入中介者来简化对象之间的复杂交互
  • 中介者模式是迪米特法则的一个典型应用
  • 对象之间多对多的复杂关系转化为相对简单的一对多关系

中介者模式的结构

image.png

中介者模式包含以下4个角色:

  • Mediator(抽象中介者)
  • ConcreteMediator(具体中介者)
  • Colleague(抽象同事类)
  • ConcreteColleague(具体同事类)

中介者类的职责

  • 中转作用(结构性):各个同事对象不再需要显式地引用其他同事,当需要和其他同事进行通信时,可通过中介者来实现间接调用
  • 协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致地和中介者进行交互,而不需要指明中介者需要具体怎么做。中介者根据封装在自身内部的协调逻辑对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装

实例学习

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

通过分析发现,在图中,界面组件之间存在较为复杂的交互关系:如果删除一个客户,将从客户列表(List)中删掉对应的项,客户选择组合框(ComboBox)中的客户名称也将减少一个;

如果增加一个客户信息,则客户列表中将增加一个客户,且组合框中也将增加一项。

为了更好地处理界面组件之间的交互,现使用中介者模式设计该系统。

抽象同事类

// 这里定义了一个注入中介者的方法

// 同事类(Colleague)只知道它的中介者,它不需要知道其他同事。

// 而且必须还有一个通知中介者的方法,不然中介者怎么知道你要进行操作。

//抽象组件类:抽象同事类
public abstract class Component {
   protected Mediator mediator;
   
   public void setMediator(Mediator mediator) {
      this.mediator = mediator;
   }

   //转发调用
   public void changed() {
      mediator.componentChanged(this);
   }
   
   public abstract void update(); 
}

具体同事类

//组合框类:具体同事类
public class ComboBox extends Component {
   public void update() {
      System.out.println("组合框增加一项:张无忌。");
   }
   
   public void select() {
      System.out.println("组合框选中项:小龙女。");
   }
}
//列表框类:具体同事类
public class List extends Component {
   public void update() {
      System.out.println("列表框增加一项:张无忌。");
   }
   
   public void select() {
      System.out.println("列表框选中项:小龙女。");
   }
}
//文本框类:具体同事类
public class TextBox extends Component {
   public void update() {
      System.out.println("客户信息增加成功后文本框清空。");
   }
   
   public void setText() {
      System.out.println("文本框显示:小龙女。");
   }
}
//按钮类:具体同事类
public class Button extends Component {
   public void update() {
      //按钮不产生响应
   }
}

抽象中介者(抽象类一般是为了扩展性)

//抽象中介者
public abstract class Mediator {
   public abstract void componentChanged(Component c);
}

具体中介者

// 中介者类需要知道所有同事类间的关系,并协调操作。

//具体中介者
public class ConcreteMediator extends Mediator {
   //维持对各个同事对象的引用
   public Button addButton;
   public List list;
   public TextBox userNameTextBox;
   public ComboBox cb;

  //封装同事对象之间的交互
   public void componentChanged(Component c) {
      //单击按钮
if (c == addButton) {
         System.out.println("--单击增加按钮--");
         list.update();
         cb.update();
         userNameTextBox.update();
      }
      //从列表框选择客户
      else if (c == list) {
         System.out.println("--从列表框选择客户--");
         cb.select();
         userNameTextBox.setText();
      }
      //从组合框选择客户
      else if (c == cb) {
         System.out.println("--从组合框选择客户--");
         cb.select();
         userNameTextBox.setText();
      }
   }
}

客户端调用类

// 为同事对象添加中介者

// 为中介者里的属性赋值

public class Client {
   public static void main(String args[]) {
        //定义中介者对象
      ConcreteMediator mediator;
      mediator = new ConcreteMediator();

        //定义同事对象
      Button addBT = new Button();
      List list = new List();
       ComboBox cb = new ComboBox();
       TextBox userNameTB = new TextBox();

      addBT.setMediator(mediator);
      list.setMediator(mediator);
      cb.setMediator(mediator);
      userNameTB.setMediator(mediator);

      mediator.addButton = addBT;
      mediator.list = list;
      mediator.cb = cb;
      mediator.userNameTextBox = userNameTB;

      addBT.changed();
      System.out.println("-----------------------------");
      list.changed();
    }
}

结果与分析

--单击增加按钮--
列表框增加一项:张无忌。
组合框增加一项:张无忌。
客户信息增加成功后文本框清空。
---------------

--从列表框选择客户--
组合框选中项:小龙女。
文本框显示:小龙女。

  • 当某个组件类的changed()方法被调用时,中介者的componentChanged()方法将被调用,在中介者的componentChanged()方法中再逐个调用与该组件有交互的其他组件的相关方法 (这里是关键步骤:当某个同事调用change方法时,中介者类就会根据change的类型去通知其它同事类
  • 如果某个组件类需要与新的组件进行交互,无须修改已有组件类的源代码,只需修改中介者或者对现有中介者进行扩展即可,系统具有更好的灵活性和可扩展性

这个案例其实用QQ的运行要更好说。

在QQ中: 中介者会根据你的操作来执行不同的方法去通知同事类进行不同的操作。
比如: 你在群里发红包,你执行了操作,中介者判断你的操作是发红包,因此整个群里的同事都能看到你的红包并进行操作。

又比如,你在群里@某人,中介者发现你是@操作,它就重点通知被@的人。

  • 当我们想增加同事,可以去继承抽象组件,然后创建新的中介者类,替换就行,符合开闭者原则。
  • 当我们想更换中介者,新建一个中介者去替换就行。

实例类介绍

(1) Mediator:抽象中介者类
(2) ConcreteMediator:具体中介者类
(3) Component:抽象组件类,充当抽象同事类
(4) Button:按钮类,充当具体同事类
(5) List:列表框类,充当具体同事类
(6) ComboBox:组合框类,充当具体同事类
(7) TextBox:文本框类,充当具体同事类
(8) Client:客户端测试类

image.png

模式优缺点

模式优点

  • 简化了对象之间的交互,它用中介者和同事的一对多交互代替了原来同事之间的多对多交互,将原本难以理解的网状结构转换成相对简单的星型结构
  • 可将各同事对象解耦
  • 可以减少子类生成,中介者模式将原本分布于多个对象间的行为集中在一起,改变这些行为只需生成新的中介者子类即可,这使得各个同事类可被重用,无须直接对同事类进行扩展

模式缺点

在具体中介者类中包含了大量的同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护

适用环境

  • 系统中对象之间存在复杂的引用关系,系统结构混乱且难以理解
  • 一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象
  • 想通过一个中间类来封装多个类中的行为,又不想生成太多的子类