内容摘录自《大话设计模式》
尽管将一个系统分割成许多对象通常可以增加其可复用性,但是对象间相互连接的激增又会降低其可复用性。因为大量的连接使得一个对象不可能在没有其他对象的支持下工作,系统表现为一个不可分割的征途,所以,对系统的行为进行任何较大的改动就十分困难了。
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。就像,国与国之间通过‘联合国’这个中介者来发生关系,而不用直接通信。
通过中介者对象,可以将系统的网状结构变成以中介者为中心的星型结构,每个具体的对象不再通过直接的联系与另一个对象发生相互作用,而是通过‘中介者’对象与另一个对象发生相互作用。中介者对象的设计,使得系统的结构不会因为新对象的引入造成大量的修改工作。
中介者模式(Mediator),用一个中介来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
Colleague叫做抽象同事类,而ConcreteColleague是具体同事类,每个具体同事只知道自己的行为,而不了解其他同事类的情况,但它们却都认识中介者对象,Mediator是抽象中介者,定义了同事对象到中介者对象的接口,ConcreteMediator是具体中介者对象,实现抽象类的方法,它需要知道所有具体同事类,并从具体同事接收消息,向具体同事发出命令。
Mediator类 抽象中介者类
abstract class Mediator
{
public abstract void Send(string message, Colleague colleague); // 定义一个抽象的发送消息方法,得到同事对象和发送信息
}
Colleague类 抽象同事类
abstract class Colleague
{
protected Mediator mediator;
public Colleague(Mediator _mediator)
{
mediator = _mediator;
}
}
ConcreteMediator类 具体中介者类
class ConcreteMediator : Mediator
{
private ConcreteColleague1 colleague1;
private ConcreteColleague2 colleague2;
public ConcreteColleague1 Colleague1 // 需要了解所有的具体同事对象
{
set { colleague1 = value; }
}
public ConcreteColleague2 Colleague2
{
set { colleague2 = value; }
}
public override void Send(string message, Colleague colleague) // 重写发送信息的方法,根据对象做出选择判断,通知对象
{
if(colleague == colleague1)
{
colleague2.Notify(message);
}
else
{
colleague1.Notify(message);
}
}
}
ConcreteColleague1和ConcreteColleague2等各种同事对象
class ConcreteColleague1 : Colleague
{
public ConcreteColleague1(Mediator _mediator):base(_mediator)
{ }
public void Send(string message)
{
mediator.Send(message, this); // 发送信息通常是中介者发送出去的
}
public void Notify(string message)
{
Debug.Log("同事1得到信息:" + message);
}
}
class ConcreteColleague2 : Colleague
{
public ConcreteColleague2(Mediator _mediator) : base(_mediator)
{ }
public void Send(string message)
{
mediator.Send(message, this);
}
public void Notify(string message)
{
Debug.Log("同事2得到信息:" + message);
}
}
客户端调用
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);
mediator.Colleague1 = colleague1;
mediator.Colleague2 = colleague2;
colleague1.Send("吃过饭了吗?");
colleague2.Send("没有呢,你打算请客?");
由于有了Mediator,使得两个具体的同事类在发送消息和接收消息时其实是通过中介者来完成的,这就减少了它们之间的耦合度了。
现在有个需求:美国和伊拉克之间的对话都是通关联合国安理会作为中介来完成。
美国和伊拉克都是国家,有一个国家抽象类和两个具体国家类就可以了。但‘联合国’到底是Mediator还是ConcreteMediator呢?
这就要取决于为啥是否有可能扩展中介者对象。除了安理会,联合国的机构还有如教科文组织、世界卫生组织等到,所以Mediator应该是‘联合国机构’,而‘安理会’是一个具体的中介者。
如果不存在扩展情况,那么Mediator可以与ConcreteMediator合二为一。
联合国机构类 相当于Mediator类
abstract class UnitedNations
{
// 声明
public abstract void Declare(string message, Country country);
}
国家类 相当于Colleague类
abstract class Country
{
protected UnitedNations unitedNations;
public Country(UnitedNations _unitedNations)
{
unitedNations = _unitedNations;
}
}
美国类 相当于ConcreteColleague1类
class USA : Country
{
public USA(UnitedNations unitedNations_) : base(unitedNations_)
{ }
// 声明
public void Declare(string message)
{
unitedNations.Declare(message, this);
}
// 获得消息
public void GetMessage(string message)
{
Debug.Log("美国获得对方信息:" + message);
}
}
伊拉克类 相当于ConcreteColleague2类
class Irag : Country
{
public Irag(UnitedNations unitedNations_) : base(unitedNations_)
{ }
// 声明
public void Declare(string message)
{
unitedNations.Declare(message, this);
}
// 获得消息
public void GetMessage(string message)
{
Debug.Log("伊拉克获得对方信息:" + message);
}
}
联合国安理会 相当于ConcreteMediator类
class UnitedNationsSecurityCouncil : UnitedNations
{
private USA country1;
private Irag country2;
public USA Country1 // 联合国安理会了解所有的国家,所以拥有美国和伊拉克的对象属性
{
set { country1 = value; }
}
public Irag Country2
{
set { country2 = value; }
}
public override void Declare(string message, Country country)
{
if(country == country1)
{
country2.GetMessage(message); // 重现了声明方法,实现了两个对象间的通信
}
else
{
country1.GetMessage(message);
}
}
}
客户端调用
UnitedNationsSecurityCouncil UNSC = new UnitedNationsSecurityCouncil();
USA country1 = new USA(UNSC);
Irag country2 = new Irag(UNSC);
UNSC.Country1 = country1;
UNSC.Country2 = country2;
country1.Declare("不准研制核武器,否则要发动战争!");
country2.Declare("我们没有核武器,也不怕侵略。");
这样的设计减少了ConcreteColleague类之间的耦合,但这又使得ConcreteMediator责任太多了,如果它出了问题,则整个系统都会有问题。
中介者模式很容易在系统中应用,也很容易在系统中误用。当系统出现了‘多对多’交互复杂的对象群时,不要急于使用中介者模式,而要先反思你的系统在设计上是不是合理。
中介者模式的优点首先是Mediator的出现减少了各个Colleague的耦合,使得可以独立地改变和复用各个Colleague类和Mediator,比如任何国家的改变不会影响到其他国家,而只是与安理会发生变化。其次,由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上来,也就是站在一个更宏观的角度去看待系统。比如巴以冲突,本来只能算是国与国之间的矛盾,因此各自的看法可能都比较狭隘,但站在联合国安理会的角度,就可以从全球化、也更客观的角度来看待这个问题,在调停和维和上做出贡献。
由于ConcreteMediator控制了集中化,于是就把交互复杂性变为了中介者的复杂性,这就使得中介者会变得比任何一个ConcreteColleague都负责。中介者模式的优点来自集中控制,其缺点也是它。
中介者模式一般应用于一组对象以定义良好但是复杂的方式进行通信的场合,以及想定制一个分布在多个类中的行为,而又不想生成太多的子类的场合。