当前位置: 代码迷 >> 综合 >> 中介者(Mediator)模式
  详细解决方案

中介者(Mediator)模式

热度:26   发布时间:2023-12-24 10:17:00.0

文章目录

  • 中介者(Mediator)模式
    • 1. 意图
    • 2. 别名
    • 3. 动机
    • 4. 适用性
    • 5. 结构
    • 6. 参与者
    • 7. 协作
    • 8. 效果
    • 9. 实现
    • 10. 代码示例
    • 11.已知应用
    • 12. 相关模式
    • 13. 设计原则口袋
    • 14. 参考文献

中介者(Mediator)模式

隶属类别——对象行为型模式


1. 意图

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

2. 别名

3. 动机

面向对象设计鼓励奖行为分布各个对象中。这种分布可能会导致对象间有许多连接。在最坏的情况下,每一个对象都知道其他所有对象。

虽然将一部分系统分割成许多对象通常可以增强可复用性,但是对象间相互连接的激增 又会降低其可复用性。大量的相互连接使得一个对象似乎不太可能在没有其他对象的支持下工作——系统表现为一个不可分割整体。而且,对系统的行为进行进行较大的改动都十分困难,因为行为被分布在许多对象中。结果是,你可能不得不定义许多子类以定制系统的行为。

例如,考虑一个图像用户界面中对话框的实现。对话框使用一个窗口来展现一系列的窗口,如按钮、菜单和输入域等,如下图所示:

在这里插入图片描述

通常对话框中的窗口组件间存在依赖关系。例如,当一个特定的输入域为空时,某个按钮不能使用;在称为列表框一列选项中选择一个表目可能会改变一个输入域的内容。反过来,在输入域中输入正文可能会自动的选择一个或多个列表框中相应的表目;一旦正文出现输入域中,其他一些按钮可能就变得能够使用了,这些按钮允许用户做一些操作,比如改变或者删除这些正文所指的东西。

不同的对话框会有不同的窗口组件间的依赖关系。因此即使对话框显示相同类型的窗口组件。也不能简单地重用已有的窗口组件类;而必须定制它们以反映特定对话框的依赖关系。由于涉及很多个类,用逐个生成子类的办法来定制它们会很冗长。

可以通过将集体行为封装在一个单独的中介者(mediator)对象中以避免这个问题。中介者负责控制和协调一组对象间的交互。中介者充当一个中介以使组中的对象不再相互显示引用。

例如,FontDialogDirectoor可作为一个对话框中的窗口组件中的中介者。FontDialogDirecotr对象知道对话框中的各窗口组件,并协调它们之间的交互,它充当窗口组件间的通信的中转中心,如下图所示。

在这里插入图片描述

下列交互图说明了各对象之间如何协作处理一个列表框中的选项变化。

在这里插入图片描述
下面一系列事件使一个列表框的选择被传送给一个输入域:

    1. 列表框告诉它的操作者它被改变了
    1. Director从列表框中得到选择的选择项
    1. Director将该选择项传递给入口域
    1. 现在入口域已有正文,Director使得用于发起一个动作(“如 “半黑体”,”斜体“)的某个(某些)按钮有用。

注意Director是如何在对话框和入口域间进行中介的。而且窗口组件间的通信都通过Director间接地进行,它们不必互相知道;它们不必互相知道;它们仅需知道Director。而且,由于所有这些行为都保持在一个类中,只要扩展或替换这个类。

这里展示的是FontDialogDirector抽象怎样被集成到一个类库中,如下图所示:
在这里插入图片描述

DialogDirector是一个抽象类,它定义了一个对话框的总体行为。客户调用showDialog操作将对话框显示在屏幕上。createWidgets是一个创建一个对话框的窗口组件的抽象操作。widgetChanged是另一个抽象操作;窗口组件调用它来通知它的Director它们被改变了。DialogDirector的子类将重定义createWidgets创建正确的窗口组件。并重新widgetChanged以处理其变化。

4. 适用性

在下列情况下适用Mediator模式:

  • 一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
  • 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
  • 想定制一个分布在多个类的行为,而又不想生成太多子类。

5. 结构

在这里插入图片描述

一个典型的对象结构可能如下图所示

在这里插入图片描述

6. 参与者

  • Mediator(中介者,如DialogDirector)
    • 中介者定义一个接口用于与各个Colleague对象通信
  • ConcreteMediator(具体中介者,如FontDialogDirector)
    • 具体中介者通过协调各同事对象实现协作行为
    • 了解并维护它的各个同事。
  • Colleague class(同事类,如ListBox,EntryField)
    • 每一个同事类都知道它的中介者对象。
    • 每一个同事对象在需与其他的同事通信的时候,与它的中介者通信。

7. 协作

  • 同事向一个中介者对象发送和接受请求。中介者在各同事间适当地转发请求以实现协作行为。

8. 效果

中介者模式有以下优点:

    1. 减少了子类生成 Mediator将原本分布于多个对象间的行为集中在一起。改变这些行为只需生成Mediator即可。这样各个Colleague类可被重用。
    1. 它将各Colleague解耦 用Mediator和各Colleague间的松耦合。你可以独立的改变和复用各Colleague类和Mediator类。
    1. 它简化了对象协议 用Mediator和各Colleague间的一对多的交互来替换多对多的交换。一对多的关系更易于理解、维护和扩展。
    1. 它对对象如何协作进行了抽象 将中介作为一个独立的概念并将其封装在一个对象中,使你将注意力从对象各自本身的行为转移到它们的交互上来。这有助于弄清楚一个系统中的对象是如何交互的。

中介者模式有以下缺点:

    1. 它使控制集中化 中介者模式将交互的复杂性变为中介者的复杂性。因为中介者封装了协议,它可能变得比任一个Colleague都复杂。这可能使得中介者自身称为一个难以维护的庞然大物。

9. 实现

下面是与中介者模式有关的一些实现问题:

    1. 忽略抽象的Mediator类 当个Colleague仅与一个Mediator一起工作时,没有必要定义一个抽象的Mediator类。Mediator类提供的抽象耦合已经使各Colleague可与不同的Mediator子类一起工作,反之亦然。
    1. Colleague——Mediator通信 当一个感兴趣的事件发生时,Colleague必须与其Mediator通信。一种实现方法是使用Observer模式,将Mediator实现为一个Observer,各Colleague作为Subject,一旦其状态改变就发送通知给Mediator。Mediator作出的响应是将状态改变的结构传播给其他的Colleague。

    另一个方法是在Mediator中定义一个特殊的通知接口,各Colleague在通信时直接调用该接口。Windows下的Smalltalk/V 使用某种形式的代理机制:当与Mediator通信时,Colleague将自身作为一个参数传递给Mediator,使其可以识别发送者。

10. 代码示例

本次使用Mediator模式实现一个聊天室

首先是Mediator——ChatMediator.java

public interface ChatMediator {
    void addUser(User user);void sendMessage(String massge, User user);
}

接下来是Colleague——User.java

public abstract class User {
    protected ChatMediator mediator;protected String name;public User(ChatMediator mediator, String name) {
    this.mediator = mediator;this.name = name;}public abstract void send(String msg);public abstract void receive(String msg, User sender);public String toString() {
    return name;}
}

然后是ConcreteMediator——ChatMediatorImpl.java

public class ChatMediatorImpl implements ChatMediator {
    List<User> users;public ChatMediatorImpl() {
    users = new ArrayList<>();}@Overridepublic final void addUser(User user) {
    users.add(user);}@Overridepublic void sendMessage(String msg, User user) {
    for (User u : users) {
    if (u != user) {
    u.receive(msg, user);}}}
}

接下来是ConcreteColleague——UserImpl.java

public class UserImpl extends User {
    public UserImpl(ChatMediator mediator, String name) {
    // initialize base classsuper(mediator,name);}@Overridepublic void send(String msg) {
    System.out.println(this.name + " : " + msg);mediator.sendMessage(msg, this);}@Overridepublic void  receive(String msg, User user) {
    System.out.println(this.name + " receiveMessage from " + user + " : " + msg);}
}

最后是Client——ChatClient.java

public class ChatCilent {
    public static void main(String[] args) {
    // TODO Auto-generated method stubChatMediator mediator = new ChatMediatorImpl();User user1 = new UserImpl(mediator, "饭团小神");User user2 = new UserImpl(mediator, "唯爱伦科");User user3 = new UserImpl(mediator, "hitman2");User user4 = new UserImpl(mediator, "化腾土狗");mediator.addUser(user1);mediator.addUser(user2);mediator.addUser(user3);mediator.addUser(user4);user1.send("大家好,我是饭团小神,我的剑姬一秒四破");user2.send("大家好,我是唯爱伦科,我的小炮贼猛");user3.send("大家好,我是hitman2,我贼喜欢用偷钱这个天赋,好用还容易上瘾");user4.send("大家好,我是化腾土狗,我的妖姬可以出肉好吧。");}
}

以及对于的实现结果:

饭团小神 : 大家好,我是饭团小神,我的剑姬一秒四破
唯爱伦科 receiveMessage from 饭团小神 : 大家好,我是饭团小神,我的剑姬一秒四破
hitman2 receiveMessage from 饭团小神 : 大家好,我是饭团小神,我的剑姬一秒四破
化腾土狗 receiveMessage from 饭团小神 : 大家好,我是饭团小神,我的剑姬一秒四破
唯爱伦科 : 大家好,我是唯爱伦科,我的小炮贼猛
饭团小神 receiveMessage from 唯爱伦科 : 大家好,我是唯爱伦科,我的小炮贼猛
hitman2 receiveMessage from 唯爱伦科 : 大家好,我是唯爱伦科,我的小炮贼猛
化腾土狗 receiveMessage from 唯爱伦科 : 大家好,我是唯爱伦科,我的小炮贼猛
hitman2 : 大家好,我是hitman2,我贼喜欢用偷钱这个天赋,好用还容易上瘾
饭团小神 receiveMessage from hitman2 : 大家好,我是hitman2,我贼喜欢用偷钱这个天赋,好用还容易上瘾
唯爱伦科 receiveMessage from hitman2 : 大家好,我是hitman2,我贼喜欢用偷钱这个天赋,好用还容易上瘾
化腾土狗 receiveMessage from hitman2 : 大家好,我是hitman2,我贼喜欢用偷钱这个天赋,好用还容易上瘾
化腾土狗 : 大家好,我是化腾土狗,我的妖姬可以出肉好吧。
饭团小神 receiveMessage from 化腾土狗 : 大家好,我是化腾土狗,我的妖姬可以出肉好吧。
唯爱伦科 receiveMessage from 化腾土狗 : 大家好,我是化腾土狗,我的妖姬可以出肉好吧。
hitman2 receiveMessage from 化腾土狗 : 大家好,我是化腾土狗,我的妖姬可以出肉好吧。

最后放上整个聊天室的类的UML图:

在这里插入图片描述

11.已知应用

ET++ 和THINK C库都在对话框中使用类似与Director对象作为窗口组件间的中介者。

Windows下的Smalltalk/V的应用结构基于中介者结构。在这个环境中,一个应用有一个包含一组窗格(pane)的窗口组成。该类库包含若干预定义的Pane对象;比如说TextPane、ListBox、Button,等等。这些窗格无需继承即可直接使用。应用开发者仅需由ViewManager衍生子类,ViewManager类复杂窗格间的协调工作。ViewManager是有一个中介者,而每一个窗格只知道它的view mangaer,它被看作该窗格的“主人”。窗格不直接互相引用。

另一个中介者模式的应用是用于协调复杂的更新。一个例子是在Observer中提到的ChangerManager类。ChangeManager在subject和Observer间进行协调以避免冗余的更新。当一个对象改变时,它通知ChangeManager,ChangeManager随即通知依赖于该对象的那些对象以协调这些更新。

一个类似的应用出现在Unidraw绘图框架中,它使用一个称为CSolver的类来实现“连接器”间的连接约束。图形编辑器中的对象可用不同的方式表现出相同依赖。连接器用于自动维护连接的应用中,如框图编辑器和电路设计系统。CSolver是连接器的中介者。它解释连接约束并更新连接器的位置以反映这些约束。

12. 相关模式

  • Facade:Facade与Mediator的不同支持在于它是一个对象子系统进行抽象,从而提供了一个方便的接口。它的协议是单向的,即Facade对象对这个子系统提出请求,但反之则不行。相反,Mediator提供了各Colleague不支持或者不能支持的协作行为,而且协议是多向的。
  • Observer : Colleague可以使用Observer模式与Mediator通信。

13. 设计原则口袋

  • 封装变化
  • 多用组合少用继承
  • 类应该对扩展开放,对修改关闭
  • 针对接口编程,不针对实现编程
  • 依赖抽象,不要依赖具体类
  • 为交互对象间的松耦合设计而努力
  • 只和密友交谈
  • 好莱坞原则——别来找我,我会来找你
  • 单一责任原则——类应该只有一个改变的理由

14. 参考文献

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

《HeadFirst设计模式》