设计模式的征途—22.中介者(Mediator)模式
我们都用过QQ,它有两种聊天方式:一是私聊,二是群聊。使用QQ群,一个用户就可以向多个用户发送相同的信息和文件,从而无需一一发送,节省大量时间。通过引入群的机制,极大地减少系统中用户之间的两两通信,用户与用户之间的联系可以通过群的机制来实现。
在有些软件中,某些类/对象之间的相互调用关系错综复杂,类似于QQ用户之间的关系,此时,特别需要一个类似“QQ群”一样的中间类来协调这些类/对象之间的复杂关系,以降低系统的耦合度。因此,一个设计模式因此诞生,它就是中介者模式。
中介者模式(Mediator) | 学习难度:★★★☆☆ | 使用频率:★★☆☆☆ |
一、客户信息管理模块的初始设计
1.1 需求背景
Background:M公司欲开发一套CRM系统,其中包含一个客户信息管理模块,所涉及的“客户信息管理窗口”界面效果图如下图所示:
M公司开发人员通过分析发现,在上图中,界面组件之间存在较为复杂的交互关系:如果删除一个客户,则将从客户列表中删掉对应的项,客户选择组合框中客户名称也称将减少一个;如果增加一个客户信息,则客户列表中将增加一个客户,且组合框中也将增加一项。
1.2 初始设计
M公司开发人员针对组件之间的交互关系进行了分析,发现:
(1)当用户单击“增加”、“删除”、“修改”或“查询”时,界面左侧的“客户选择组合框”、“客户列表”以及界面中的文本框将产生响应。
(2)当用户通过”客户选择组合框“选中某个客户姓名时,”客户列表“和文本框将产生响应。
(3)当用户通过“客户列表”选中某个客户姓名时,“客户选择组合框”和文本框将产生响应。
根据交互关系,开发人员绘制了如下图所示的初始类图。
不难发现,上述设计存在以下问题:
(1)系统结构负责且耦合度高 => 复杂的网状结构,OMG!
(2)组件的可重用性差 => 由于每一个组件和其他组件之间都具有很强的关联,很难重用!
(3)系统的扩展性差 => 如果在上述系统中增加一个新的组件类,必须修改与之交互的各个组件源代码!
二、中介者模式概述
2.1 中介者模式简介
如果在一个系统中对象之间存在多对多的相互关系,可以将对象之间的一些交互行为从各个对象中分离出来,并集中封装在一个中介者对象中,并由该中介者进行统一协调,这样对象之间多对多的复杂关系就转化为了相对简单的一对多关系。通过引入中介者来简化对象之间的复杂关系,它是迪米特法则的一个典型应用。
中介者(Mediator)模式:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以相对独立地改变它们之间的交互。中介者模式又称为调停模式,它是一种对象行为型模式。
2.2 中介者模式结构
在中介者模式中,引入了用于协调其他对象/类之间的相互调用的中介者类,为了让系统具有更好的灵活性和可扩展性,通常还提供了抽象中介者,其结构图如下图所示:
中介者模式结构图中主要包含以下4个角色:
(1)Mediator(抽象中介者):它定义了一个接口,该接口用于与各同事对象之间进行通信。
(2)ConcreteMediator(具体中介者):它实现了接口,通过协调各个同事对象来实现协作行为,维持了各个同事对象的引用。
(3)Colleague(抽象同事类):它定义了各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时维持了一个对抽象中介者类的引用,其子类可以通过该引用来与中介者通信。
(4)ConcreteColleague(具体同事类):抽象同事类的子类,每一个同事对象需要和其他对象通信时,都需要先与中介者对象通信,通过中介者来间接完成与其他同事类的通信。
三、客户信息管理模块的重构实现
3.1 重构后的设计结构
M公司开发人员借助中介者模式来重新设计结构如下图所示:
在具体实现时,为了确保系统有更好的灵活性和可扩展性,需要定义抽象中介者和抽象组件类,其中抽象组件类是所有具体组件类的公共父类,完整类图如下图所示:
其中,Component充当抽象同事类,Button,List,ComboBox和TextBox充当具体同事类,Mediator充当抽象中介者类,ConcreteMediator充当具体中介类。在ConcreteMediator中维持了对具体同事对象的引用,为了简化ConcreteMediator类的代码,在其中只定义了一个Button对象和TextBox对象。
3.2 重构后的代码实现
(1)抽象中介者:Mediator
/// <summary>
/// 抽象中介者
/// </summary>
public abstract class Mediator
{
public abstract void ComponenetChanged(Component c);
}
(2)具体中介者:ConcreteMediator
/// <summary>
/// 具体中介者
/// </summary>
public class ConcreteMediator : Mediator
{
// 维持对各个同事对象的引用
public Button addButton;
public List list;
public TextBox userNameTextBox;
public ComboBox cb; // 封装同事对象之间的交互
public override void ComponenetChanged(Component c)
{
// 单击按钮
if (c == addButton)
{
Console.WriteLine("-- 单击增加按钮 --");
list.Update();
cb.Update();
userNameTextBox.Update();
}
// 从列表框选择客户
else if (c == list)
{
Console.WriteLine("-- 从列表框选择客户 --");
cb.Select();
userNameTextBox.SetText();
}
// 从组合框选择客户
else if (c == cb)
{
Console.WriteLine("-- 从组合框选择客户 --");
cb.Select();
userNameTextBox.SetText();
}
}
}
(3)抽象同事类:Colleague
/// <summary>
/// 抽象同事类:抽象组件
/// </summary>
public abstract class Component
{
protected Mediator mediator; public void SetMediator(Mediator mediator)
{
this.mediator = mediator;
} // 转发调用
public void Changed()
{
mediator.ComponenetChanged(this);
} public abstract void Update();
}
(4)具体同事类:ConcreteColleague
/// <summary>
/// 具体同事类:按钮组件
/// </summary>
public class Button : Component
{
public override void Update()
{
// 按钮不产生响应
}
} /// <summary>
/// 具体同事类:列表框组件
/// </summary>
public class List : Component
{
public override void Update()
{
Console.WriteLine("列表框增加一项:张无忌");
} public void Select()
{
Console.WriteLine("列表框选中项:小龙女");
}
} /// <summary>
/// 具体同事类:组合框组件
/// </summary>
public class ComboBox : Component
{
public override void Update()
{
Console.WriteLine("组合框增加一项:张无忌");
} public void Select()
{
Console.WriteLine("组合框选中项:小龙女");
}
} /// <summary>
/// 具体同事类:文本框组件
/// </summary>
public class TextBox : Component
{
public override void Update()
{
Console.WriteLine("客户信息增加成功后文本框清空");
} public void SetText()
{
Console.WriteLine("文本框显示:小龙女");
}
} /// <summary>
/// 具体同事类:标签组件
/// </summary>
public class Label : Component
{
public override void Update()
{
Console.WriteLine("文本标签内容改变,客户信息总数量加1");
}
}
(5)客户端测试:
public class Program
{
public static void Main()
{
// Step1.定义中介者对象
ConcreteMediator mediator = new ConcreteMediator(); // Step2.定义同事对象
Button addButton = new Button();
List list = new List();
ComboBox cb = new ComboBox();
TextBox userNameTextBox = new TextBox(); addButton.SetMediator(mediator);
list.SetMediator(mediator);
cb.SetMediator(mediator);
userNameTextBox.SetMediator(mediator); mediator.addButton = addButton;
mediator.list = list;
mediator.cb = cb;
mediator.userNameTextBox = userNameTextBox; // Step3.点击增加按钮
addButton.Changed(); Console.WriteLine("---------------------------------------------"); // Step4.从列表框选择客户
list.Changed();
}
}
调试运行后的结果如下所示:
四、中介者模式小结
4.1 主要优点
(1)简化了对象之间的交互,它用中介者和同事的一对多交互替代了原来同事之间的多对多交互。
(2)将各同事对象解耦,可以独立地改变和复用每个同事和中介者,增加新的中介和同事很方便,符合开闭原则。
(3)可以减少大量同事子类的生成,改变同事行为只需要生成新的中介者子类即可。
4.2 主要缺点
具体中介者子类中包含了大量的同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护。
4.3 应用场景
(1)系统中对象之间存在复杂的引用关系 => 系统结构混乱且难以理解
(2)一个对象由于引用了其他很多对象并且直接和这些对象通信 => 难以复用该对象
(3)想要通过一个中间类来封装多个类的行为又不想生成太多子类 => 引入中介者即可实现
参考资料
(1)刘伟,《设计模式的艺术—软件开发人员内功修炼之道》
设计模式的征途—22.中介者(Mediator)模式的更多相关文章
- 设计模式C++描述----18.中介者(Mediator)模式
一. 举例 比如,现在中图和日本在关于钓鱼岛问题上存在争端.这时,联合国就会站出来,做为调解者,其实也没什么好调解的,钓鱼岛本来就是中国的,这是不争的事实!联合国也就是个传话者.发言人. 结构图如下: ...
- 重构if...else...或者switch程序块 为 中介者(Mediator)模式.的思考
http://www.cnblogs.com/insus/p/4142264.html 重构if...else...或者switch程序块 为 中介者(Mediator)模式.的思考 首先普世的编程架 ...
- 设计模式的征途(C#实现)—文章目录索引
1.预备篇 UML类图10分钟快速入门 2.创建型模式 ① 设计模式的征途-01.单例(Singleton)模式 ② 设计模式的征途-02.简单工厂(Simple Factory)模式 ③ 设计模式的 ...
- 设计模式--中介(Mediator)模式
时隔很长一段时,现在又重温设计模式,上个星期学习<设计模式--代理(Proxy)模式>http://www.cnblogs.com/insus/p/4128814.html. 温故而知新, ...
- Java设计模式(16)中介模式(Mediator模式)
Mediator定义:用一个中介对象来封装一系列关于对象交互行为. 为何使用Mediator模式/中介模式 各个对象之间的交互操作非常多,每个对象的行为操作都依赖彼此对方,修改一个对象的行为,同时会涉 ...
- C#设计模式之十八中介者模式(Mediator Pattern)【行为型】
一.引言 今天我们开始讲“行为型”设计模式的第五个模式,该模式是[中介者模式],英文名称是:Mediator Pattern.还是老套路,先从名字上来看看.“中介者模式”我第一次看到这个名称,我的理解 ...
- Mediator 中介者 协调者模式
简介 定义:用一个[中介者对象]封装一系列的[对象交互],中介者使各对象不需要显示地相互作用,从而使耦合松散,而且可以独立地改变它们之间的交互. 中介者模式的结构 抽象中介者Mediator:定义好[ ...
- 设计模式之第22章-组合模式(Java实现)
设计模式之第22章-组合模式(Java实现) “鱼哥,有没有什么模式是用来处理树形的“部分与整体”的层次结构的啊.”“当然”“没有?”“有啊.别急,一会人就到了.” 组合模式之自我介绍 “请问你是?怎 ...
- Mediator模式(仲裁者设计模式)
Mediator ? Mediator的意思是"仲裁者""中介者".一方面,当发生麻烦事情的时候,通知仲裁者:当发生涉及全体组员的事情时,也通知仲裁者.当仲裁者 ...
随机推荐
- Linux 中环境变量设置
本文主要整理自以下博文: .bash_profile和.bashrc的什么区别及启动过程 linux环境变量设置方法总结(PATH/LD_LIBRARY_PATH) .bash_profile 和 . ...
- startService与bindService的区别
转自:http://www.devdiv.com/thread-52226-1-1.html Service的生命周期方法比Activity少一些,只有onCreate, onStart, onDes ...
- OSB开发常用资料
成功搭建OSB环境并运行HelloWorld项目 http://www.beansoft.biz/?p=2066 Oracle Service Bus 11gR1开发环境安装文档 http://www ...
- Android Studio使用Lint进行代码检查
Android Studio目前已经更新到1.4版本,它作为Google官方推荐的IDE,功能非常强大,其中提供了一套静态代码分析工具,它可以帮助我们检查项目中存在的问题,让我们更有规范性的开发App ...
- SlopOne 改进
lope One 其基本的想法来自于简单的一元线性模型 $w = f(v) = v + b$.已知一组训练点 ${(v_i, w_i)}_{i=1}^n$,利用此线性模型最小化预测误差的平方和,我们可 ...
- LeetCode之“数组”:Rotate Array
题目链接 题目要求: Rotate an array of n elements to the right by k steps. For example, with n = 7 and k = 3, ...
- Gradle 1.12用户指南翻译——第三十三章. PMD 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- 能量最小化初探,graphcuts能量最小化调用
1.相对于能量函数来说,能量最小化的办法都有哪些? 梯度下降 模拟退火 图割 2.这个 跟最优化问题的求解,有什么联系跟区别呢? 基本上差不多,其实就是求出来了函数的一个最小值,我们看问题的时候不妨把 ...
- App Store10大被拒理由
最近,苹果在官网给出了截至2015年2月份应用被拒绝的十大理由,其中50%以上的应用被拒绝都是因为这10个原因,其中7个理由和2014年相同,其中排名前三的原因分别是:需要补充更多信息.存在明显的bu ...
- 面试之路(7)-BAT面试题之计算机的三大原则
1.计算机是执行输入.运算.输出的机器 计 算 机 的 硬 件 由 大 量 的 IC(Integrated Circuit,集成电路)组成.每块 IC 上都带有许多引脚.这些引脚有的用于输入,有的用于 ...