房东:到处找租客可太麻烦, 直接交给中介, 交给他们管理吧~
意图
用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用, 从而使其耦合松散, 而且可以独立地改变它们之间的交互.
动机
GoF 中提到的一点, 我觉得很有道理, 面向对象设计鼓励将行为分布到各个对象中.这种分布可能会导致对象间有许多连接.在最坏的情况下, 每一个对象都知道其他所有对象.
我点击数字按键 Button
, LineEdit
上增加对应的数字, 点击删除按键, LineEdit
就会删除最后一位, LineEdit
中为空的时候, 某个按钮不能使用等等
我们不可能让 LineEdit
这个类去监听所有按键的点击事件, 统筹所有的交互行为, 一方面这只是计算器界面中组件间依赖关系, 是一种特定的依赖关系, 另一方面 LineEidt
这个类其实不关心组件中这么复杂的依赖关系, 如果需要交由 LineEdit
统筹, 那就需要在各种各样的场景中定制 LineEidt
控件, 但是并不仅仅只有 LineEidt
会和其他组件会有依赖关系, 这样的设计只会让代码显得越来越乱
所以就需要引入一个单独的中介者 (mediator
) 对象以避免这个问题, 中介者负责控制和协调一组对象间的交互, 中介者充当一个中介以使组中的对象不再相互显式引用. 这些对象仅知道中介者, 从而减少了相互连接的数目.
UML 图
Mediator
(中介者)
- 中介者定义一个接口用于与各个同事 (
Colleague
) 对象通信
- 中介者定义一个接口用于与各个同事 (
ConcreteMediator
(具体中介者)- 具体中介者通过协调各个同事对象实现协作行为
- 了解并维护它的各个同事
Colleague
(同事类)- 每一个同事类都知道它的中介者对象
- 每一个同事对象在需要与其他的同事通信的时候, 以它的中介者通信
从 UML 图中可以看出以下几点
ConcreteMediator
等于是持有所有需要交互的对象, 由它来负责所有交互, 很明显的优点是Colleague
对象变得简洁了,Colleague
间的耦合松散了, 但是中介者就变得特别复杂了, 等于是将原本Colleague
间交互的复杂性转嫁给了中介者, 这可能导致最后中介者本身变成一个难以维护的庞然大物- 抽象的
Mediator
和抽象的Colleague
, 在这里我觉得更多是定义一种通信的接口,Mediator
中定义一个特殊的通知接口, 各Colleague
在通信时调用该接口, 这是一种很简单的思路
此外也可以使用观察者模式去实现这种通信,Mediator
是一个观察者,Colleague
是一个Subject
, 一旦其状态改变的就发送通知给Mediator
, 通知可以直接将需要传递的信息也携带 (推模式), 也可以仅仅是通知然后让Mediator
自己来取 (拉模式), 所以这种结构一开始让我感觉特别相似观察者模式
, 连 UML 图都很像
应用场景
- 一组对象以定义良好但是复杂的方式进行通信. 产生的相互依赖关系结构混乱且难以理解.
- 一个对象引用其他很多对象并且直接与这些对象通信, 导致难以复用该对象.
- 想定制一个分布在多个类中的行为, 而又不想生成太多的子类.
举个例子
直接使用前面动机中的例子, 但是我们简化一下
我们只有2个数字按键 (
Button
), 分别是 1 和 2, 以及一个显示点击数字结果的LineEdit
, 每次点击按钮之后将按钮的值加到显示控件最后一位
首先定义中介者的基类 Mediator
, 它需要定义一个收到按键触发的函数 buttonChanged()
class Widget;
class Mediator {
public:
Mediator() {}
virtual ~Mediator() {}
virtual void buttonChanged(Widget *) = 0;
};
然后定义 Button
和 LineEdit
的共同基类 Widget
, 它需要知道谁来协调它 Mediator
, 并且在点击的时候, 通知 Mediator
, 本来这里应该是一个类似 MouseEvent
事件的函数, 在这个函数中调用 m_mediator->buttonChanged(this);
, 但是这只是一个例子, 我们就简单一点
#include "mediator.h"
class Widget {
public:
Widget(Mediator *mediator) : m_mediator(mediator) {}
virtual ~Widget(){}
virtual void Click() {
m_mediator->buttonChanged(this);
}
private:
Mediator *m_mediator;
};
接下来, 我么先将具体的 Button
和 LineEdit
实现出来
#include "widget.h"
#include <string>
using namespace std;
class Button : public Widget {
public:
Button(Mediator *mediator, string showinfo) : Widget(mediator), m_showinfo(showinfo) {}
void setShowInfo(string info) { m_showinfo = info;}
string getShowInfo() { return m_showinfo;}
private:
string m_showinfo;
};
class LineEdit : public Widget {
public:
LineEdit(Mediator *mediator) : Widget(mediator), m_showinfo(""){}
void setShowInfo(string info) { m_showinfo = info;}
string getShowInfo() { return m_showinfo;}
private:
string m_showinfo;
};
接下来就是具体的中介者, 因为它需要统筹, 我就让它直接初始化 2 个按键和 LineEdit
, 顺便添加了2个模拟点击的接口和一个显示 LineEdit
值的接口
#define SAFE_DELETE(p) if(p) {delete p; p=nullptr;}
#include "concretecolleague.h"
#include <iostream>
using namespace std;
class ConcreteMediator : public Mediator {
public:
ConcreteMediator() :
m_number1(new Button(this, string("1")))
, m_number2(new Button(this, string("2")))
, m_lineedit(new LineEdit(this))
{}
~ConcreteMediator() {
SAFE_DELETE(m_number1);
SAFE_DELETE(m_number2);
SAFE_DELETE(m_lineedit);
}
void clickButton1() {
m_number1->Click();
}
void clickButton2() {
m_number2->Click();
}
void showLineEditInfo() {
cout << m_lineedit->getShowInfo() << endl;
}
virtual void buttonChanged(Widget *widget) {
string new_info = m_lineedit->getShowInfo();
if (widget == m_number1) {
string info = m_number1->getShowInfo();
new_info += info;
} else if (widget == m_number2) {
string info = m_number2->getShowInfo();
new_info += info;
}
m_lineedit->setShowInfo(new_info);
}
private:
Button *m_number1;
Button *m_number2;
LineEdit *m_lineedit;
};
ps: 这里 buttonChanged(Widget *widget)
函数中在这个例子是可以不用 if...else...
这种形式, 因为使用的接口都是一样
最后就是 main
函数的调用
#include "concretemediator.h"
int main()
{
ConcreteMediator mediator;
mediator.clickButton1();
mediator.clickButton1();
mediator.clickButton1();
mediator.showLineEditInfo();
mediator.clickButton2();
mediator.clickButton2();
mediator.clickButton2();
mediator.clickButton2();
mediator.showLineEditInfo();
return 0;
}
运行结果
111
1112222
总结
将复杂的交互部分整理成一个中介对象, 由中介对象来承担复杂的变化, 但是让它统筹的对象达到松耦合的目标, 整体结构也更加灵活~