随着越来越多的类在程序中被开发出来,这些类之间的通信问题变得越来越复杂。每个类需要知道其他类的方法越多,类的结构就容易变得混乱,这使得程序不易于阅读和维护。另外,对程序的修改也变得困难起来,因为任何修改都会影响到其他多个类的代码。中介者模式通过促进这些类之间更加松散的耦合来解决这一问题。中介者实现这样一做法的方式是,成为唯一一个详细了解其他类的方法的类,其他的类在改变发生的时候通知中介者,中介者把这些变动传递给其他任何需要被通知的类。
我们的例子中,是这样的一个程序,用户界面如图所示:
当程序启动的时候,两个按钮都被禁用,当有选手被选中的时候copy按钮可以使用,当右侧列表有信息了以后clear按钮可用,clear以后两个按钮都不可用。
这样在这里可视化空间的交互非常复杂,交互关系图:
中介者模式简化了这一系统,方法是其作为唯一知晓系统内的其他的类的类存在。每个与中介者通信的空间都成为同事,每个同时在接收到某些用户事件以后通知中介者,接着中介者决定应该向其他哪些类通知该事件。
中介者的优点很明显,踏实唯一了解其他类的类,因此,如果其他类中的某一个发生了变化,或者添加了其他的界面控件类,中介者就是唯一休要修改的类。
这个例程中还是用了命令模式将按钮的事件和要处理的代码分离开来。
定义一个命令模式中要使用的共同接口:
using System;
using CsharpPats;
namespace Mediate
{
/// <summary>
/// Command interface
/// </summary>
public interface Command
{
void Execute();
}
}
先是构建了一个复制按钮类:CpyButton
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
namespace Mediate
{
/// <summary>
/// Button command passes its click to the mediator
/// </summary>
public class CpyButton : System.Windows.Forms.Button, Command {
private Container components = null;
private Mediator med;
//-----
public CpyButton() {
InitializeComponent();
}
//-----
public void setMediator(Mediator md) {
med = md;
}
//-----
public void Execute() {
med.copyClicked ();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}
这里的执行函数通过中介者来实现。
其他按钮的定义:
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
namespace Mediate
{
/// <summary>
/// Summary description for ClrButton.
/// </summary>
public class ClrButton : System.Windows.Forms.Button , Command
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
private Mediator med;
//-----
public ClrButton() {
InitializeComponent();
}
//-----
public void setMediator(Mediator md) {
med = md;
}
//-----
public void Execute() {
med.clearClicked ();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}
这里还用到了上一节介绍的迭代器模式,用来迭代游泳选手的数据。
然后定义了一个KidList列表控件:
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
namespace Mediate
{
/// <summary>
/// Summary description for KidList.
/// </summary>
public class KidList : System.Windows.Forms.ListBox, Command
{
private System.ComponentModel.Container components = null;
private Mediator med;
//-----
public KidList() {
InitializeComponent();
}
//-----
public void Execute() {
med.kidPicked ();
}
//-----
public void setMediator(Mediator md) {
med = md;
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}
然后我们就开始构造中介者了,中介者需要给他传递中介者需要调节的所有控件的引用:
using System;
using System.Windows.Forms;
namespace Mediate
{
/// <summary>
/// Receives all command actions from buttons and lists
/// and sends commands to all the relevant controls
/// </summary>
public class Mediator {
private CpyButton cpButton; //buttons
private ClrButton clrButton;
private TextBox txKids; //text box
private ListBox pkList; //list boxes
private KidList klist;
private KidData kds; //list of data from file
public Mediator(CpyButton cp, ClrButton clr, KidList kl, ListBox pk) {
cpButton = cp; //copy in buttons
clrButton = clr;
klist = kl; //copy in list boxes
pkList = pk;
kds = new KidData ("50free.txt"); //create data list class
clearClicked(); //clear all controls
KidIterator kiter = kds.getIterator ();
while(kiter.MoveNext () ) { //load list box
Kid kd = (Kid) kiter.Current ;
klist.Items .Add (kd.getFrname() +" "+kd.getLname ());
}
}
//-----
//get text box reference
public void setText(TextBox tx) {
txKids = tx;
}
//-----
//clear lists and set buttons to disabled
public void clearClicked() {
//disable buttons and clear list
cpButton.Enabled = false;
clrButton.Enabled = false;
pkList.Items.Clear();
}
//-----
//copy data from text box to list box
public void copyClicked() {
//copy name to picked list
pkList.Items.Add(txKids.Text);
//clear button enabled
clrButton.Enabled = true;
klist.SelectedIndex = -1;
}
//-----
//copy selected kid to text box
//enable copy button
public void kidPicked() {
//copy text from list to textbox
txKids.Text = klist.Text;
//copy button enabled
cpButton.Enabled = true;
}
}
}
这里的中介者中包含了所有的控件(就是我们这里所谓的同事)将来发送给中介者的事件,然后中介者对事件做出对应的响应。
在中介者里包含了左侧游泳选手列表的构造。
然后就是处理界面按钮事件,因为我们使用了命令模式,所以可以将用户按钮都指定到一个事件句柄中,提供统一的处理接口。
private Mediate.KidList lsKids;
private Mediate.CpyButton btCopy;
private Mediate.ClrButton btClear;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
private Mediator med;
public Form1() {
InitializeComponent();
init();
}
//-----
private void init() {
//set up mediator and pass in referencs to controls
med = new Mediator (btCopy, btClear, lsKids, lsSelected);
btCopy.setMediator (med); //set mediator ref in each control
btClear.setMediator (med);
lsKids.setMediator (med);
med.setText (txName); //tell mediator about text box
//create event handler for all command objects
EventHandler evh = new EventHandler (clickHandler);
btClear.Click += evh;
btCopy.Click += evh;
lsKids.SelectedIndexChanged += evh;
}
public void clickHandler(object obj, EventArgs e) {
Command comd = (Command)obj; //get command object
comd.Execute (); //and execute command
}
我们最好把各个控件的初始化操作委托给中介者,这样中介者可以初始化所有的需要控制的控件的状态:
//clear lists and set buttons to disabled
public void clearClicked() {
//disable buttons and clear list
cpButton.Enabled = false;
clrButton.Enabled = false;
pkList.Items.Clear();
}
中介者避免了类之间的纠缠不清,使得修改程序变得更加容易。可以轻松的添加其他的控件或者其他类,然而如果中介者需要服务的同事类太多的话,那么中介者无疑需要会包含非常多的处理代码,可能会让中介者这个类的可读性非常差,不易于理解。
这里我们可以看到,现在的例程中,我们包含了越来越多的设计模式,在这个中介者模式中,还使用了迭代器模式,命令模式两种模式,以后的例子中可能会经常需要更多的模式合作来完成,这在应用程序开发的过程中很常见,也更加符合OOP的思想。