• 面向对象编程思想-中介者模式


    一、引言

    前两天休息日在网上打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();
            }
        }
    View Code

    分析:上述确实解决了场景中的问题,牌友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();
            }
        }  
    View Code

    分析:运行结果和上面不使用中介者模式的代码运行结果一致,这样如果某个牌友类变化时,只会影响到该变化的牌友类和中介者类,从而解决上面代码中的问题。

    但是此时如果要新增一个牌友类,我们就不得不修改抽象中介者类,还可以再完善一下吗?完全可以,我们可以结合观察者模式,在抽象中介者类中保存一个抽象牌友类的集合,添加保存删除牌友的方法管理集合,然后在具体的中介者类中,修改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();
            }
        }
    View Code

    优点:

    1.将系统各个对象之间的关系进行封装,将各个同事类解耦,使系统松耦合

    2.将对象间一对多关系转变为一对一关联,使对象间关系易于理解和维护

    3.提高系统灵活性,使各个同事类独立而易于复用

    缺点:

    1.中介者承担的责任太重,一旦中介者出现问题,整个系统将会受到影响

    2.新增加一个同事类时,不得不修改抽象中介者类和具体的中介者类

    适用场景:

    1.一组定义好的关系,要进行复杂的通信(如最开始对象间关系成网状结构)时,使用中介者模式可以使同事类间关系更清晰

    2.想通过一个中间类来封装多个类中的行为

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

    参照:

    1.大话设计模式

    2.http://www.cnblogs.com/zhili/p/MediatorPattern.html

  • 相关阅读:
    Dubbo架构设计及原理详解
    Zookeeper+Dubbo+SpringMVC环境搭建
    Java 延迟队列使用
    深入理解Spring Redis的使用 (九)、通过Redis 实现 分布式锁 的 BUG,以及和数据库加锁的性能测试
    深入理解Spring Redis的使用 (八)、Spring Redis实现 注解 自动缓存
    深入理解Spring Redis的使用 (七)、Spring Redis 使用 jackson序列化 以及 BaseDao代码
    深入理解Spring Redis的使用 (六)、用Spring Aop 实现注解Dao层的自动Spring Redis缓存
    深入理解Spring Redis的使用 (五)、常见问题汇总
    深入理解Spring Redis的使用 (四)、RedisTemplate执行Redis脚本
    深入理解Spring Redis的使用 (三)、使用RedisTemplate的操作类访问Redis
  • 原文地址:https://www.cnblogs.com/jdzhang/p/7405736.html
Copyright © 2020-2023  润新知