意图

中介者模式是一种行为型模式,能让你减少对象之间混乱无序的依赖关系。该模式会限制对象之间的直接交互,迫使它们通过一个中介者对象进行合作。

问题

假如你有一个创建和修改客户资料的对话框,它由各种控件组成,例如文本框(TextField)、复选框(Checkbox)和按钮(Button)等。

用户界面中各元素间的关系会随程序发展而变得混乱

某些表单元素可能会直接进行互动。例如,选中“我有一只狗”复选框后可能会显示一个隐藏文本框用于输入狗狗的名字。另一个例子是提交按钮必须在保存数据前校验所有输入内容。

元素间存在许多关联。因此,对某些元素进行修改可能会影响其他元素

如果直接在表单元素代码中实现业务逻辑,你将很难在程序其他表单中复用这些元素类。例如,由于复选框类与狗狗的文本框相耦合,所以将无法在其他表单中使用它。你要么使用渲染资料表单时用到的所有类,要么一个都不用。

解决方案

中介者模式建议你停止组件之间的直接交流并使其相互独立。这些组件必须调用特殊的中介者对象,通过中介者对象重定向调用行为,以间接的方式进行合作。最终,组件仅依赖于一个中介者类,无需与多个其他组件相耦合。

在资料编辑表单的例子中,对话框(Dialog)类本身将作为中介者,其很可能已知自己所有的子元素,因此你甚至无需在该类中引入新的依赖关系。

UI 元素必须通过中介者对象进行间接沟通

绝大部分重要的修改都在实际表单元素中进行。让我们想想提交按钮。之前,当用户点击按钮后,它必须对所有表单元素数值进行校验。而现在它的唯一工作是将点击事件通知给对话框。收到通知后,对话框可以自行校验数值或将任务委派给各元素。这样一来,按钮不再与多个表单元素相关联,而仅依赖于对话框类。

你还可以为所有类型的对话框抽取通用接口,进一步削弱其依赖性。接口中将声明一个所有表单元素都能使用的通知方法,可用于将元素中发生的事件通知给对话框。这样一来,所有实现了该接口的对话框都能使用这个提交按钮了。

采用这种方式,中介者模式让你能在单个中介者对象中封装多个对象间的复杂关系网。类所拥有的依赖关系越少,就越易于修改、扩展或复用。

结构

  1. 组件(Component)是各种包含业务逻辑的类。每个组件都有一个指向中介者的引用,该引用被声明为中介者接口类型。组件不知道中介者实际所属的类,因此你可通过将其连接到不同的中介者以使其能在其他程序中复用。
  2. 中介者(Mediator)接口声明了与组件交流的方法,但通常仅包括一个通知方法。组件可将任意上下文(包括自己的对象)作为该方法的参数,只有这样接收组件和发送者类之间才不会耦合。
  3. 具体中介者(Concrete Mediator)封装了多种组件间的关系。具体中介者通常会保存所有组件的引用并对其进行管理,甚至有时会对其生命周期进行管理。
  4. 组件并不知道其他组件的情况。如果组件内发生了重要事件,它只能通知中介者。中介者收到通知后能轻易地确定发送者,这或许已足以判断接下来需要触发的组件了。
    对于组件来说,中介者看上去完全就是一个黑箱。发送者不知道最终会由谁来处理自己的请求,接收者也不知道最初是谁发出了请求。

实现方式

  1. 找到一组当前紧密耦合,且提供其独立性能带来更大好处的类(例如更易于维护或更方便复用)。
  2. 声明中介者接口并描述中介者和各种组件之间所需的交流接口。在绝大多数情况下,一个接收组件通知的方法就足够了。如果你希望在不同情景下复用组件类,那么该接口将非常重要。只要组件使用通用接口与其中介者合作,你就能将该组件与不同实现中的中介者进行连接。
  3. 实现具体中介者类。该类可从自行保存其下所有组件的引用中受益。
  4. 你可以更进一步,让中介者负责组件对象的创建和销毁。此后,中介者可能会与工厂或外观类似。
  5. 组件必须保存对于中介者对象的引用。该连接通常在组件的构造函数中建立,该函数会将中介者对象作为参数传递。
  6. 修改组件代码,使其可调用中介者的通知方法,而非其他组件的方法。然后将调用其他组件的代码抽取到中介者类中,并在中介者接收到该组件通知时执行这些代码。

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using System;

namespace RefactoringGuru.DesignPatterns.Mediator.Conceptual
{
// The Mediator interface declares a method used by components to notify the
// mediator about various events. The Mediator may react to these events and
// pass the execution to other components.
public interface IMediator
{
void Notify(object sender, string ev);
}

// Concrete Mediators implement cooperative behavior by coordinating several
// components.
class ConcreteMediator : IMediator
{
private Component1 _component1;

private Component2 _component2;

public ConcreteMediator(Component1 component1, Component2 component2)
{
this._component1 = component1;
this._component1.SetMediator(this);
this._component2 = component2;
this._component2.SetMediator(this);
}

public void Notify(object sender, string ev)
{
if (ev == "A")
{
Console.WriteLine("Mediator reacts on A and triggers folowing operations:");
this._component2.DoC();
}
if (ev == "D")
{
Console.WriteLine("Mediator reacts on D and triggers following operations:");
this._component1.DoB();
this._component2.DoC();
}
}
}

// The Base Component provides the basic functionality of storing a
// mediator's instance inside component objects.
class BaseComponent
{
protected IMediator _mediator;

public BaseComponent(IMediator mediator = null)
{
this._mediator = mediator;
}

public void SetMediator(IMediator mediator)
{
this._mediator = mediator;
}
}

// Concrete Components implement various functionality. They don't depend on
// other components. They also don't depend on any concrete mediator
// classes.
class Component1 : BaseComponent
{
public void DoA()
{
Console.WriteLine("Component 1 does A.");

this._mediator.Notify(this, "A");
}

public void DoB()
{
Console.WriteLine("Component 1 does B.");

this._mediator.Notify(this, "B");
}
}

class Component2 : BaseComponent
{
public void DoC()
{
Console.WriteLine("Component 2 does C.");

this._mediator.Notify(this, "C");
}

public void DoD()
{
Console.WriteLine("Component 2 does D.");

this._mediator.Notify(this, "D");
}
}

class Program
{
static void Main(string[] args)
{
// The client code.
Component1 component1 = new Component1();
Component2 component2 = new Component2();
new ConcreteMediator(component1, component2);

Console.WriteLine("Client triggets operation A.");
component1.DoA();

Console.WriteLine();

Console.WriteLine("Client triggers operation D.");
component2.DoD();
}
}
}

执行结果:

1
2
3
4
5
6
7
8
9
10
Client triggers operation A.
Component 1 does A.
Mediator reacts on A and triggers following operations:
Component 2 does C.

Client triggers operation D.
Component 2 does D.
Mediator reacts on D and triggers following operations:
Component 1 does B.
Component 2 does C.

参考原文:中介者设计模式