Intent
定义一个对象封装一系列对象的交互。Mediator 通过避免对象间显式的引用彼此降低耦合度,且能够独立的变更它们间的交互。
Motivation
面向对象的设计鼓励对象间行为的分发。这种分发会产生一个对象间有很多连接的对象结构;在最糟糕的情况下,每个对象最终都会知道彼此的存在。
虽然将一个系统拆分为很多对象通常能提升可重用性,激增的互联却常常减少了这种可重用性。许多互联使一个对象在没有其他对象的支持下工作变得更不可能 - 系统表现得好像它是一个整体。更进一步,没有什么效果显著得方法来改变系统的行为,因为行为分布在众多的对象之中。结果,你可能被迫定义很多子类来定制系统的行为。
举个例子,假设有一个图像用户界面的对话框。一个对话框使用窗口来表示 widgets ,比如按钮,菜单等,的集合,如下所示:
对话框中的 widgets 通常是有依赖关系的。比如,当特定的条目字段为空时,对应的按钮被禁用。包含的 widget 完全相同的对话框也不能简单的重用现存的 widgets 类,因为 widgets 间的关系可能不同。通过子类化分别定制 widgets class 是十分冗长的,因为这涉及很多子类。
通过将行为集合封装到一个单独的 mediator 对象中可以避免这些问题。mediator 负责控制和协调组内对象的交互。mediator 作为一个中介保证组内对象不会显式的引用彼此。对象仅知道 mediator,因此减少了互联的数量。
例如,FontDialogDirector 能作为对话框中的 widgets 之间的 mediator。一个 FontDiaglogDirectior 对象知道对话框中的 widgets 并协助它们交互。它成为了这些 widgets 交流的枢纽:
下面的交互图解释了对象间如何相互协作来处理当 list box 的选择发生改变的情况:
Widgets 彼此间的通讯是间接的,通过 director 完成。它们不必知道彼此的存在;它们所知道的仅仅只有 director。更进一步,因为行为被本地化在一个类中,通过替换或拓展该类来改变或者替换这些行为。
下面是 FontDiaglogDirector 抽象如何被集成到类库中:
Applicability
使用 Mediator 模式当
- 一系列的对象使用定义好的但是复杂的方式进行通讯。导致相互依赖关系是非结构化的且难以理解。
- 重用对象变得困难,因为它引用很多其他的对象并与之通讯。
- 分布在几个类中的行为应当在不使用多个子类的情况下定制。
Structure
一个典型的对象结构可能看起来像这样:
Participants
- Mediator(DialogDirector)
- 定义与 Colleague 对象通讯的接口。
- ConcreteMediator(FontDialogDirector)
- 通过协调 Colleague 对象实现合作行为。
- 知道和管理它的 Colleague。
- Colleague classes(ListBox,EntryField)
- 每个 Colleague 类都知道它的 Mediator 对象。
- 无论何时当 colleague 需要与另一个 colleague 通讯时,它都与它的 mediator 通讯。
Collaborations
- Colleagues 向 Mediator 对象发送和接收请求。mediator 通过在合适的 colleagues 之间路由请求来实现合作行为。
Consequence
Mediator 模式有下列优点和缺点:
- It limits subclassing。mediator 对分布在多个对象中的行为进行本地化。改变这一行为仅需要子类化 Mediator 即可。Colleague 类能被直接重用。
- It decouples colleagues。mediator 促进 colleagues 间的松散耦合。你可以独立改变或重用 Colleague 和 Mediator 类。
- It simplifies object protocols。mediator 替换 many-to-many 交互为 mediator 和 colleagues 之间的 one-to-many 交互。one-to-many 的关系更易于理解,管理和拓展。
- It abstracts how objects cooperate。使 mediation 成为一个独立的概念并将它封装到一个对象中让你可以不考虑对象的个体行为,专注于对象如何交互。这可能有助于阐明对象在系统中如何交互。
- It centralizes control。Mediator 模式用交互的复杂性换取 mediator 中的复杂性。因为 mediator 封装了协议,它可能变得比每个个体 colleague 都复杂。这可能使 mediator 本身变成庞然大物而难以管理。
Implementation
下述实现问题与 Mediator 模式相关:
- Omitting the abstract Mediator class。当 colleagues 只使用一个 mediator 工作时,不需要定义一个抽象的 Mediator 类。Mediator 类提供抽象耦合来使 colleagues 与不同的 Mediator 子类工作,反之亦然。
- Colleague-Mediator 通讯。当感兴趣的事发生时,colleagues 必须与它们的 mediator 通讯。一种方法是使用 Observer 模式实现 Mediator。Colleague 类作为 Subjects,无论何时当它们的状态发生变化时,向 mediator 发送通知。mediator 通过向其他的 colleague 传播改变的影响来作为响应。
另一个方法是在 Mediator 中定义一个特殊的通知接口,使 colleagues 在它们的通讯中变得更加直接。当与 mediator 通讯时,colleagues 将他自己作为参数传递,以允许mediator 来标识 sender。
Sample Code
class DialogDirector {
public:
virtual ~DialogDirector();
virtual void ShowDialog();
virtual void WidgetChanged(Widget*) = 0;
protected:
DialogDirector();
virtual void CreateWidgets() = 0;
};
class Widget {
public:
Widget(DialogDirector*);
virtual void Changed();
virtual void HandleMouse(MouseEvent& event);
// ...
private:
DialogDirector* _director;
};
void Widget::Changed () {
_director->WidgetChanged(this);
}
class ListBox : public Widget {
public:
ListBox(DialogDirector*);
virtual const char* GetSelection();
virtual void SetList(List<char*>* listItems);
virtual void HandleMouse(MouseEvent& event);
// ...
};
class FontDialogDirector : public DialogDirector {
public:
FontDialogDirector();
virtual ~FontDialogDirector();
virtual void WidgetChanged(Widget*);
protected:
virtual void CreateWidgets();
private:
Button* _ok;
Button* _cancel;
ListBox* _fontList;
EntryField* _fontName;
};
void FontDialogDirector::WidgetChanged ( Widget* theChangedWidget ) {
if (theChangedWidget == _fontList) {
_fontName->SetText(_fontList->GetSelection());
} else if (theChangedWidget == _ok) {
// apply font change and dismiss dialog
// ...
} else if (theChangedWidget == _cancel) {
// dismiss dialog
}
}