步步为营 .NET 设计模式学习笔记 二十、Mediator(中介者模式)
概述
在软件构建过程中,经常会出现多个对象互相关联交互的情况,
对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。
在这种情况下,我们可使用一个“中介对象”来管理对象间的关联关系,避免相互交互的对象之间的紧耦合引用关系,从而更好地抵御变化。
意图
用一个中介对象来封装一系列的对象交互。
中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
结构图
角色说明:
Mediator:抽象调停者角色,它定义出同事对象到调停者对象得接口,这个角色一般由Java抽象类实现
ConcreteMediator:具体调停者模式,从抽象调停者继承,实现了超类所声明的事件方法,它从具体同事对象接收消息,向具体同事对象发出命令.
Colleague:抽象同事类角色,它定义了调停者同事对象的接口,它只知道调停者而不知道其余同事对象.
colleague1、colleague2:具体同事类角色,从抽象同事类继承而来,每个具体同事类都知道本身在小范围内的行为,而不知道它在大范围内的目的.
生活中的例子
现在大街上,有很多婚姻中介,一般是单身男女把各自的资料存放在婚介处,单身男女自己选择自己有意对像进行比对,如果某对男女互想感兴趣,则安排它们见面交谈,感觉良好就速成,不好就继续配对.
示例用例图
刚来到这个地方工作时,就面临租房问题,当时我人生地不熟,找了个中介,给我找了个离公司300米左右的单间,根据这个,我们设计了中介者模式的用例图,用例图如下:
代码设计:
先创建RentingMediator.cs:
public abstract class RentingMediator { public abstract string Notice(string message,House house); }
再创建House.cs:
public abstract class House { protected RentingMediator mediator; public House(RentingMediator rentingMediator) { this.mediator = rentingMediator; } public abstract string GetMessage(string message); }
再创建Renter.cs:
public class Renter : House { public Renter(RentingMediator mediator) : base(mediator) { } public string Notice(string message) { return mediator.Notice(message, this); } public override string GetMessage(string message) { return string.Format("租房者接收消息:{0}\n", message); } }
再创建Landlord.cs:
public class Landlord : House { public Landlord(RentingMediator mediator) : base(mediator) { } public string Notice(string message) { return mediator.Notice(message, this); } public override string GetMessage(string message) { return string.Format("房东接收消息:{0}\n", message); } }
再创建IntelligenceMediator.cs:
public class IntelligenceMediator : RentingMediator { private Renter _renter; private Landlord _landlord; public Renter renter { set { _renter = value; } get { return _renter; } } public Landlord landlord { set { _landlord = value; } get { return _landlord; } } public override string Notice(string message, House house) { if (house == renter) { return landlord.GetMessage(message); } else { return renter.GetMessage(message); } } }
最后再调用:
public partial class Run : Form { public Run() { InitializeComponent(); } private void btnRun_Click(object sender, EventArgs e) { //------------------------------------- IntelligenceMediator mediator = new IntelligenceMediator(); Renter zhangshang = new Renter(mediator); Landlord lishi = new Landlord(mediator); mediator.renter = zhangshang; mediator.landlord = lishi; rtbResult.AppendText( zhangshang.Notice("带空调、独卫和阳台的25平米的房间多少钱?")); rtbResult.AppendText(lishi.Notice("800元每月,水费15元,物业费15元,电费0.8元每度.")); } }
运行结果如下图:
实现要点
1.在C#中可以适用delegate关联中介者和各同事之间的交互行为,这样各同事就不需要直接和中介者进行耦合。
2.中介者模式和观察者模式的区别是,前者应用于多对多杂乱交互行为的统筹处理,后者应用于一(多)对多关系的灵活定制。对于本例来说,集中处理后还需要分散处理,那么后半阶段的处理过程可以应用观察者模式。对于前一节的例子来说,如果有多个主体角色和多个观察者进行多对多通讯的话,也可以应用中介者模式来统筹这个多对多的过程(大家可以自己尝试修改前一节的实例来应用中介者模式)。
3.中介者模式和门面模式的区别是,前者的各同事类需要依靠中介者进行双向通讯,应用于子系统之间,而后者的子系统往往不会通过门面去和调用方进行通讯,趋向于单向通讯,应用于子系列和更高层次的系统。本例中就有门面模式和中介者模式的影子。
4.中介者模式往往可以在构架的层次进行应用,有的时候和观察者模式以及门面模式一起使用,有的时候又会向观察者模式和门面模式退化。其实在应用模式的过程中不必过多考虑模式的准确定位,如果我们确实从中得以,那么这个名字就不重要了。
5.将多个对象间复杂的关联关系解耦,Mediator模式将多个对象间的控制逻辑进行集中管理, 变“多个对象互相关联”为“多个对象和一个中介者关联”,简化了系统的维护,抵御了可能的变化。
6.随着控制逻辑的复杂化,Mediator具体对象的实现可能相当复杂。这时候可以对Mediator对象进行分解处理。
7.Façade模式是解耦系统外到系统内(单向)的对象关联关系;Mediator模式是解耦系统内各个对象之间(双向)的关联关系。
适用性
1.如果一组接口相对稳定的对象之间的依赖关系错综复杂,依赖关系难以维护,或者会发生变动可以考虑引入中介者模式。
2.一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
3.一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
4.想定制一个分布在多个类中的行为,而又不想生成太多的子类。
优点
1.减少了子类生成。Mediator将原本分布于多个对象间的行为集中在一起,改变这些行为只需生成Mediator的子类即可,这样各个Colleague类可被重用。
2.它将各Colleague解耦。Mediator有利于各Colleague间的松耦合,你可以独立的改变和复用各Colleague类和Mediator类。
3.它简化了对象协议。用Mediator和各Colleague间的一对多的交互来代替多对多的交互。一对多的关系更易于理解、维护和扩展。
4.它对对象如何协作进行了抽象。将中介作为一个独立的概念并将其封装在一个对象中,使你将注意力从对象各自本身的行为转移到它们之间的交互上来。这有助于弄清楚一个系统中的对象是如何交互的。
5.它使控制集中化。中介者模式将交互的复杂性变为中介者的复杂性。因为中介者封装了协议,它可能变得比任一个Colleague都复杂。这可能使得中介者自身成为一个难于维护的庞然大物。
总结
1.不是所有的系统都需要应用中介者模式把多对多的关系转化为多对一对多的。如果各个同事之间本来的关联就很清晰(没有交错关联),或这种关联并不复杂,没有必要应用中介者。
2.在实际的应用过程中,中介者做的控制并不会向本例那样简单,它可能包含很多的处理逻辑。如果还伴随着需求的改变,中介者角色可能会越来越难维护,此时可以考虑对中介者角色或处理行为应用其它的一些设计模式。