0%

中介者模式(Mediator)

引子

在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是“网状结构”,它要求每个对象都必须知道它需要交互的对象。例如,每个人必须记住他(她)所有朋友的电话;而且,朋友中如果有人的电话修改了,他(她)必须让其他所有的朋友一起修改,这叫作“牵一发而动全身”,非常复杂。

如果把这种“网状结构”改为“星形结构”的话,将大大降低它们之间的“耦合性”,这时只要找一个“中介者”就可以了。如前面所说的“每个人必须记住所有朋友电话”的问题,只要在网上建立一个每个朋友都可以访问的“通信录”就解决了。这样的例子还有很多,例如,你刚刚参加工作想租房,可以找“房屋中介”;或者,自己刚刚到一个陌生城市找工作,可以找“人才交流中心”帮忙。

在软件的开发过程中,这样的例子也很多,例如,在 MVC 框架中,控制器(C)就是模型(M)和视图(V)的中介者;还有大家常用的 QQ 聊天程序的“中介者”是 QQ 服务器。所有这些,都可以采用“中介者模式”来实现,它将大大降低对象之间的耦合性,提高系统的灵活性。

中介者模式(Mediator)

定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象的耦合度,使原有对象之间不必户互了解。

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。

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

主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。

何时使用:多个类相互耦合,形成了网状结构。

如何解决:将上述网状结构分离为星型结构。

关键代码:对象 Colleague 之间的通信封装到一个类中单独处理。

定义与特点

定义

定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。

主要优点
  1. 类之间各司其职,符合迪米特法则。
  2. 降低了对象之间的耦合性,使得对象易于独立地被复用。
  3. 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
主要缺点

中介者会庞大,变得复杂难以维护。

中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变得复杂且难以维护。

结构与实现

结构

中介者模式实现的关键是找出“中介者”,下面对它的结构和实现进行分析。

中介者模式包含以下主要角色。

  1. 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
  2. 具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
  3. 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
  4. 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

设计图

实现

写实现代码

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
using System;
using System.Collections.Generic;

namespace MediatorPattern
{
/// <summary>
/// 抽象中介类
/// </summary>
interface IMediator
{
/// <summary>
/// 注册
/// </summary>
/// <param name="colleague"></param>
void Register(Colleague colleague);
/// <summary>
/// 转发
/// </summary>
/// <param name="colleague"></param>
void Relay(Colleague colleague);
}

class ConcreteMediator : IMediator
{
/// <summary>
/// 关联的同事信息
/// </summary>
private List<Colleague> Colleagues = new List<Colleague>();

/// <summary>
/// 注册
/// </summary>
/// <param name="colleague"></param>
public void Register(Colleague colleague)
{
if (!Colleagues.Contains(colleague))
{
Colleagues.Add(colleague);
colleague.SetMedium(this);
}
}

/// <summary>
/// 转发
/// </summary>
/// <param name="colleague"></param>
public void Relay(Colleague colleague)
{
Colleagues.ForEach(c =>
{
if (c != colleague)
{
c.Receive();
}
});
}
}
/// <summary>
/// 抽象同事类
/// </summary>
abstract class Colleague
{
protected IMediator mediator;
public void SetMedium(IMediator mediator)
{
this.mediator = mediator;
}
public abstract void Receive();
public abstract void Send();
}

class ConcreteColleague1 : Colleague
{
public override void Receive()
{
Console.WriteLine("具体同事类1收到请求。");
}

public override void Send()
{
Console.WriteLine("具体同事类1发出请求。");
mediator.Relay(this); //请中介者转发
}
}

class ConcreteColleague2 : Colleague
{
public override void Receive()
{
Console.WriteLine("具体同事类2收到请求。");
}

public override void Send()
{
Console.WriteLine("具体同事类2发出请求。");
mediator.Relay(this); //请中介者转发
}
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;

namespace MediatorPattern
{
class Program
{
static void Main(string[] args)
{
IMediator md = new ConcreteMediator();
Colleague c1, c2;
c1 = new ConcreteColleague1();
c2 = new ConcreteColleague2();
md.Register(c1);
md.Register(c2);
c1.Send();
Console.WriteLine("-------------");
c2.Send();

Console.ReadLine();
}
}
}

应用实例

【例1】用中介者模式编写一个“韶关房地产交流平台”程序。

说明:韶关房地产交流平台是“房地产中介公司”提供给“卖方客户”与“买方客户”进行信息交流的平台,比较适合用中介者模式来实现。

首先,定义一个中介公司(Medium)接口,它是抽象中介者,它包含了客户注册方法 register(Customer member) 和信息转发方法 relay(String from,String ad);再定义一个韶关房地产中介(EstateMedium)公司,它是具体中介者类,它包含了保存客户信息的 List 对象,并实现了中介公司中的抽象方法。

然后,定义一个客户(Customer)类,它是抽象同事类,其中包含了中介者的对象,和发送信息的 send(String ad) 方法与接收信息的 receive(String from,String ad) 方法的接口,由于本程序是窗体程序,所以本类继承 JPmme 类,并实现动作事件的处理方法 actionPerformed(ActionEvent e)。

最后,定义卖方(Seller)类和买方(Buyer)类,它们是具体同事类,是客户(Customer)类的子类,它们实现了父类中的抽象方法,通过中介者类进行信息交流,其结构图如图 2 所示。

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
using System.Collections.Generic;

namespace MediatorPattern2
{
interface IMediator
{
/// <summary>
/// 注册
/// </summary>
/// <param name="member"></param>
void Register(ICustomer member);
/// <summary>
/// 转发
/// </summary>
/// <param name="member"></param>
/// <param name="ad"></param>
void Relay(ICustomer member, string ad);
}

class EstateMedium : IMediator
{
List<ICustomer> customers = new List<ICustomer>();
public void Register(ICustomer member)
{
if (!customers.Contains(member))
{
customers.Add(member);
member.Mediator = this;
}
}

public void Relay(ICustomer member, string ad)
{
customers.ForEach(customer =>
{
if (member != customer)
{
customer.Receive(member.NickName, ad);
}
});
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace MediatorPattern2
{
/// <summary>
/// 抽象客户构件
/// </summary>
interface ICustomer
{
IMediator Mediator { get; set; }

string NickName { get; set; }

void Send(string ad);
void Receive(string from, string ad);
}
}

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
118
using System.Windows.Forms;

namespace MediatorPattern2
{
abstract class Customer : Form, ICustomer
{
private RichTextBox receiveTextBox;
private GroupBox groupBox2;
private RichTextBox sendTextBox;
private GroupBox groupBox1;
private string nickName;

public IMediator Mediator { get; set; }
public string NickName
{
get
{
return nickName;
}
set
{
if (nickName == value)
return;
nickName = value;
this.Text = value;
}
}

public abstract void Receive(string from, string ad);

public abstract void Send(string ad);

protected void AppendMsg(string msg)
{
receiveTextBox.AppendText(msg);
//使滚动条滚动到最底端
receiveTextBox.ScrollToCaret();
}

public Customer()
{
InitializeComponent();
}

private void InitializeComponent()
{
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.receiveTextBox = new System.Windows.Forms.RichTextBox();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.sendTextBox = new System.Windows.Forms.RichTextBox();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.SuspendLayout();
//
// groupBox1
//
this.groupBox1.Controls.Add(this.receiveTextBox);
this.groupBox1.Location = new System.Drawing.Point(12, 12);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(256, 334);
this.groupBox1.TabIndex = 0;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "接收内容";
//
// receiveTextBox
//
this.receiveTextBox.Enabled = false;
this.receiveTextBox.Location = new System.Drawing.Point(6, 20);
this.receiveTextBox.Name = "receiveTextBox";
this.receiveTextBox.Size = new System.Drawing.Size(244, 308);
this.receiveTextBox.TabIndex = 0;
this.receiveTextBox.Text = "";
//
// groupBox2
//
this.groupBox2.Controls.Add(this.sendTextBox);
this.groupBox2.Location = new System.Drawing.Point(12, 357);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(256, 64);
this.groupBox2.TabIndex = 1;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "发送内容";
//
// sendTextBox
//
this.sendTextBox.Location = new System.Drawing.Point(6, 20);
this.sendTextBox.Name = "sendTextBox";
this.sendTextBox.Size = new System.Drawing.Size(244, 34);
this.sendTextBox.TabIndex = 0;
this.sendTextBox.Text = "";
this.sendTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.sendTextBox_KeyDown);
//
// Customer
//
this.ClientSize = new System.Drawing.Size(280, 433);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Location = new System.Drawing.Point(20, 20);
this.Name = "Customer";
this.groupBox1.ResumeLayout(false);
this.groupBox2.ResumeLayout(false);
this.ResumeLayout(false);
}

private void sendTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
string msg = sendTextBox.Text.Trim();
if (string.IsNullOrEmpty(msg))
return;
Send(msg);
sendTextBox.Clear();
}
}
}
}

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
namespace MediatorPattern2
{
class Seller : Customer
{
public override void Receive(string from, string ad)
{
string appendText = (from + "说: " + ad + "\n");
AppendMsg(appendText);
}

public override void Send(string ad)
{
string appendText = "我(卖方)说: " + ad + "\n";
AppendMsg(appendText);

Mediator.Relay(this, ad);
}
}

class Buyer : Customer
{
public override void Receive(string from, string ad)
{
string appendText = (from + "说: " + ad + "\n");
AppendMsg(appendText);
}

public override void Send(string ad)
{
string appendText = "我(买方)说: " + ad + "\n";
AppendMsg(appendText);

Mediator.Relay(this, ad);
}
}
}

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
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace MediatorPattern2
{
/// <summary>
/// Winform 多窗口启动
/// </summary>
class MultiFormApplictionStart : ApplicationContext
{
/*
private void onFormClosed(object sender, EventArgs e)
{
if (Application.OpenForms.Count == 0)
{
ExitThread();
}
}*/
/*
private void onFormClosed(object sender, EventArgs e)
{
ExitThread();
}*/

List<Form> formList = new List<Form>();

public void AddChildForm(Form form)
{
if (formList.Contains(form))
return;
formList.Add(form);
form.FormClosed += OnMainFormClosed;
form.Show();
}
}
}
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MediatorPattern2
{
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);


IMediator md = new EstateMedium(); //房产中介
Customer member1, member2;
member1 = new Seller();
member1.NickName = "张三(卖方)";
member2 = new Buyer();
member2.NickName = "李四(买方)";
md.Register(member1); //客户注册
md.Register(member2);

MultiFormApplictionStart multiFormApplictionStart = new MultiFormApplictionStart();
multiFormApplictionStart.AddChildForm(member1);
multiFormApplictionStart.AddChildForm(member2);

Application.Run(multiFormApplictionStart);


}
}
}

应用场景

1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。
2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

注意事项:不应当在职责混乱的时候使用。

扩展

http://c.biancheng.net/view/1393.html

欢迎关注我的其它发布渠道