一、引言
前两天休息日在网上打QQ斗地主,每盘结束后腾讯游戏平台会自动计算输赢的欢乐豆,嗯?挺好的,平时在面对面玩斗地主时,一盘游戏结束后,我们需要了解每个人的出牌状况,然后算出来输赢。现在有了游戏平台,玩家之间计算输赢这个操作交给了游戏平台,我们不再需要了解每个人的出牌状况。在软件设计中,我们将解决这种业务场景的模式称之为中介者模式
二、中介者模式
定义:用一个中介对象来封装一系列对象的交互。中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且独立改变它们之间的交互。
下面是中介者模式结构图:
为什么要使用中介者模式?
如果不使用中介者模式,各个同事对象将相互引用,如果每个对象都与多个对象交互时,将形成下图所示网状结构:
分析:上图中每个对象之间过度耦合,不利于类的复用也不利于扩展。假如对象一变化了将可能引起与它直接相依赖的所有对象的调整,所以同事对象之间直接依赖不是好的设计。
如果采用中介者模式,那么对象之间的关系将变成下图所示星型结构;
分析:使用中介者模式后,任何一个同事对象的变化,只会影响中介者和类本身,不会像之前设计中一个对象变化,将会引用所有与之关联的对象变化
场景:两个人本金都是100,在一起打牌,每盘结束后计算两个人的金额
下面是不使用中介者模式的demo:
//抽象同事类 充当牌友类 public abstract class Colleague { public int Number { get; set; } public abstract void Win(int number, Colleague colleague); } //牌友A类 public class ConcreteColleagueA : Colleague { public override void Win(int number, Colleague colleague) { this.Number += number; colleague.Number -= number; } } //牌友B类 class ConcreteColleagueB : Colleague { public override void Win(int number, Colleague colleague) { this.Number += number; colleague.Number -= number; } } class Program { static void Main(string[] args) { //A和B两个人打牌 Colleague colleagueA = new ConcreteColleagueA(); Colleague colleagueB = new ConcreteColleagueB(); colleagueA.Number = 100; colleagueB.Number = 100; //A赢了B就输钱 colleagueA.Win(5, colleagueB); Console.WriteLine($"A的数量为{colleagueA.Number}"); Console.WriteLine($"B的数量为{colleagueA.Number}"); Console.Read(); } }
分析:上述确实解决了场景中的问题,牌友A和牌友B都依赖于抽象,从而降低同事类之间的耦合度。这样的设计中,牌友A的变化会引起牌友B的变化,但是如果现在要新增一个牌友C、D、E呢?设计到多个对象的变化时,当前同事类需要了解其他所有涉及的同事类状况,这时就需要修改同事类了,而且,多个牌友计算输赢时任何一个人计算错误,都会导致整个业务场景中的计算错误。基于此思考,能不能把计算的钱的任务交给程序做呢?譬如QQ斗地主。。。即引入中介者对象来协调各个对象之间的关系
下面是引入中介者对象的代码demo
//抽象同事类 public abstract class Colleague { public int Number { get; set; } public abstract void Win(int number, Mediator mediator); } //抽象中介者类 public abstract class Mediator { public ConcreteColleagueA A; public ConcreteColleagueB B; public Mediator(ConcreteColleagueA a, ConcreteColleagueB b) { A = a; B = b; } public abstract void AWin(int number); public abstract void BWin(int number); } public class ConcreteColleagueA : Colleague { public override void Win(int number, Mediator mediator) { mediator.AWin(number); } } public class ConcreteColleagueB : Colleague { public override void Win(int number, Mediator mediator) { mediator.BWin(number); } } class ConcreteMediator : Mediator { public ConcreteMediator(ConcreteColleagueA a, ConcreteColleagueB b) : base(a,b) { } public override void AWin(int number) { A.Number += number; B.Number -= number; } public override void BWin(int number) { A.Number -= number; B.Number += number; } } class Program { static void Main(string[] args) { ConcreteColleagueA concreteColleagueA = new ConcreteColleagueA(); ConcreteColleagueB concreteColleagueB = new ConcreteColleagueB(); ConcreteMediator concreteMediator = new ConcreteMediator(concreteColleagueA, concreteColleagueB); concreteColleagueA.Number = 100; concreteColleagueB.Number = 100; concreteMediator.AWin(5); Console.WriteLine($"A的数量为{concreteColleagueA.Number}"); Console.WriteLine($"B的数量为{concreteColleagueB.Number}"); Console.WriteLine(); Console.Read(); } }
分析:运行结果和上面不使用中介者模式的代码运行结果一致,这样如果某个牌友类变化时,只会影响到该变化的牌友类和中介者类,从而解决上面代码中的问题。
但是此时如果要新增一个牌友类,我们就不得不修改抽象中介者类,还可以再完善一下吗?完全可以,我们可以结合观察者模式,在抽象中介者类中保存一个抽象牌友类的集合,添加保存删除牌友的方法管理集合,然后在具体的中介者类中,修改AWin()、BWin()循环遍历执行改变自己和其他牌友的钱数。此时仍然存在一个问题,新增牌友时,我们虽然不需要修改抽象中介者类,但是还是要在具体中介者类中添加CWin()方法,这时可以此采用状态模式来解决问题,具体会在下一个专题中介绍。
下面是大话设计模式中的例子辅助理解中介者模式:
abstract class Colleague { protected Mediator mediator; public Colleague(Mediator mediator) { this.mediator = mediator; } } abstract class Mediator { public abstract void Send(string message, Colleague colleague); } class ConcreteColleagueA : Colleague { public ConcreteColleagueA(Mediator mediator) : base(mediator) { } public void Send(string message) { mediator.Send(message, this); } public void Notify(string message) { Console.WriteLine("同事A得到消息:"+message); } } class ConcreteColleagueB : Colleague { public ConcreteColleagueB(Mediator mediator) : base(mediator) { } public void Send(string message) { mediator.Send(message, this); } public void Notify(string message) { Console.WriteLine("同事B得到消息:" + message); } } class ConcreteMediator : Mediator { private ConcreteColleagueA concreteColleagueA; private ConcreteColleagueB concreteColleagueB; public ConcreteColleagueA ConcreteColleagueA { set { concreteColleagueA = value; } } public ConcreteColleagueB ConcreteColleagueB { set { concreteColleagueB = value; } } public override void Send(string message, Colleague colleague) { if(colleague==concreteColleagueA) { concreteColleagueB.Notify(message); } else if(colleague==concreteColleagueB) { concreteColleagueA.Notify(message); } } } class Program { static void Main(string[] args) { ConcreteMediator concreteMediator = new ConcreteMediator(); ConcreteColleagueA concreteColleagueA = new ConcreteColleagueA(concreteMediator); ConcreteColleagueB concreteColleagueB = new ConcreteColleagueB(concreteMediator); concreteMediator.ConcreteColleagueA = concreteColleagueA; concreteMediator.ConcreteColleagueB = concreteColleagueB; concreteColleagueA.Send("你吃过饭了吗?"); concreteColleagueB.Send("我吃过了"); Console.Read(); } }
优点:
1.将系统各个对象之间的关系进行封装,将各个同事类解耦,使系统松耦合
2.将对象间一对多关系转变为一对一关联,使对象间关系易于理解和维护
3.提高系统灵活性,使各个同事类独立而易于复用
缺点:
1.中介者承担的责任太重,一旦中介者出现问题,整个系统将会受到影响
2.新增加一个同事类时,不得不修改抽象中介者类和具体的中介者类
适用场景:
1.一组定义好的关系,要进行复杂的通信(如最开始对象间关系成网状结构)时,使用中介者模式可以使同事类间关系更清晰
2.想通过一个中间类来封装多个类中的行为
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
参照:
1.大话设计模式
2.http://www.cnblogs.com/zhili/p/MediatorPattern.html