zoukankan      html  css  js  c++  java
  • DesignPattern(五)行为型模式(上)

    行为型模式

           行为型模式是对在不同对象之间划分责任和算法的抽象化。行为模式不仅仅关于类和对象,还关于它们之间的相互作用。行为型模式又分为类的行为模式和对象的行为模式两种。

    类的行为模式——使用继承关系在几个类之间分配行为。

    对象的行为模式——使用对象聚合的方式来分配行为。

           行为型模式包括11种模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、状态模式、策略模式、责任链模式、访问者模式、解释器模式和备忘录模式。

    模板方法模式:封装算法结构,定义算法骨架,支持算法子步骤变化。

    命令模式:注重将请求封装为对象,支持请求的变化,通过将一组行为抽象为对象,实现行为请求者和行为实现者之间的解耦。

    迭代器模式:注重封装特定领域变化,支持集合的变化,屏蔽集合对象内部复杂结构,提供客户程序对它的透明遍历。

    观察者模式:注重封装对象通知,支持通信对象的变化,实现对象状态改变,通知依赖它的对象并更新。

    中介者模式:注重封装对象间的交互,通过封装一系列对象之间的复杂交互,使他们不需要显式相互引用,实现解耦。

    状态模式:注重封装与状态相关的行为,支持状态的变化,通过封装对象状态,从而在其内部状态改变时改变它的行为。

    策略模式:注重封装算法,支持算法的变化,通过封装一系列算法,从而可以随时独立于客户替换算法。

    责任链模式:注重封装对象责任,支持责任的变化,通过动态构建职责链,实现事务处理。

    访问者模式:注重封装对象操作变化,支持在运行时为类结构添加新的操作,在类层次结构中,在不改变各类的前提下定义作用于这些类实例的新的操作。

    备忘录模式:注重封装对象状态变化,支持状态保存、恢复。

    解释器模式:注重封装特定领域变化,支持领域问题的频繁变化,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。

    模板方法模式

      在现实生活中,有论文模板,简历模板等。在现实生活中,模板的概念是给定一定的格式,然后其他所有使用模板的人可以根据自己的需求去实现它。同样,模板方法也是这样的。

    模板方法模式是在一个抽象类中定义一个操作中的算法骨架,而将一些具体步骤实现延迟到子类中去实现。模板方法使得子类可以不改变算法结构的前提下,重新定义算法的特定步骤,从而达到复用代码的效果。具体的结构图如下所示(以生活中做菜为例子实现的模板方法结构图)。

    模板方法示例代码

    // 客户端调用
    class Client
    {
        static void Main(string[] args)
        {
            // 创建一个菠菜实例并调用模板方法
            Spinach spinach = new Spinach();
            spinach.CookVegetabel();
            Console.Read();
        }
    }
    
    public abstract class Vegetabel
    {
        // 模板方法,不要把模版方法定义为Virtual或abstract方法,避免被子类重写,防止更改流程的执行顺序
        public void CookVegetabel()
        {
            Console.WriteLine("抄蔬菜的一般做法");
            this.pourOil();
            this.HeatOil();
            this.pourVegetable();
            this.stir_fry();
        }
        // 第一步倒油
        public  void pourOil()
        {
            Console.WriteLine("倒油");
        }
        // 把油烧热
        public  void HeatOil()
        {
            Console.WriteLine("把油烧热");
        }
        // 油热了之后倒蔬菜下去,具体哪种蔬菜由子类决定
        public abstract void pourVegetable();
        // 开发翻炒蔬菜
        public  void stir_fry()
        {
            Console.WriteLine("翻炒");
        }
    }
    
    // 菠菜
    public class Spinach : Vegetabel
    {
       
        public override void pourVegetable()
        {
            Console.WriteLine("倒菠菜进锅中");
        }
    }
    // 大白菜
    public class ChineseCabbage : Vegetabel
    {      
        public override void pourVegetable()
        {
            Console.WriteLine("倒大白菜进锅中");
        }
    }
    模板方法示例代码

    模板方法模式中涉及了两个角色:

    抽象模板角色(Vegetable扮演这个角色):定义了一个或多个抽象操作,以便让子类实现,这些抽象操作称为基本操作。

    具体模板角色(ChineseCabbage和Spinach扮演这个角色):实现父类所定义的一个或多个抽象方法。

    模板方法模式在抽象类中定义了算法的实现步骤,将这些步骤的实现延迟到具体子类中去实现,从而使所有子类复用了父类的代码,所以模板方法模式是基于继承的一种实现代码复用的技术。

    模板方法模式的优缺点

    优点:

    实现了代码复用

    能够灵活应对子步骤的变化,符合开放-封闭原则

    缺点:因为引入了一个抽象类,如果具体实现过多的话,需要用户或开发人员需要花更多的时间去理清类之间的关系。

    附:在.NET中模板方法的应用也很多,例如我们在开发自定义的Web控件或WinForm控件时,我们只需要重写某个控件的部分方法。


     命令模式

           命令模式属于对象的行为模式,命令模式把一个请求或操作封装到一个对象中,通过对命令的抽象化来使得发出命令的责任和执行命令的责任分隔开。命令模式的实现可以提供命令的撤销和恢复功能。具体的结构图如下所示。

     

    院领导说计算机学院要进行军训,计算机学院的学生要跑1000米,院领导的话也就相当于一个命令,他不可能直接传达给到学生,他必须让教官来发出命令,并监督学生执行该命令。在这个场景中,发出命令的责任是属于学院领导,院领导充当与命令发出者的角色,执行命令的责任是属于学生,学生充当于命令接收者的角色,而教官就充当于命令的发出者或命令请求者的角色,命令模式的精髓就在于把每个命令抽象为对象。

    命令模式示例代码

    // 教官,负责调用命令对象执行请求
    public class Invoke
    {
        public Command _command;
        public Invoke(Command command)
        {
            this._command = command;
        }
        public void ExecuteCommand()
        {
            _command.Action();
        }
    }
    // 命令抽象类
    public abstract class Command 
    {
        // 命令应该知道接收者是谁,所以有Receiver这个成员变量
        protected Receiver _receiver;
        public Command(Receiver receiver)
        {
            this._receiver = receiver;
        }
        // 命令执行方法
        public abstract void Action();
    }
    // 
    public class ConcreteCommand :Command
    {
        public ConcreteCommand(Receiver receiver)
            : base(receiver)
        { 
        }
        public override void Action()
        {
            // 调用接收的方法,因为执行命令的是学生
            _receiver.Run1000Meters();
        }
    }
    // 命令接收者——学生
    public class Receiver
    {
        public void Run1000Meters()
        {
            Console.WriteLine("跑1000米");
        }
    }
    
    // 院领导
    class Program
    {
        static void Main(string[] args)
        {
            // 初始化Receiver、Invoke和Command
            Receiver r = new Receiver();
            Command c = new ConcreteCommand(r);
            Invoke i = new Invoke(c);
            
            // 院领导发出命令
            i.ExecuteCommand();
        }
    }
    命令模式示例代码

    命令模式的应用

    在ASP.NET的MVC模式中,有一种叫Front Controller的模式,它分为Handler和Command树两个部分,Handler处理所有公共的逻辑,接收HTTP Post或Get请求以及相关的参数并根据输入的参数选择正确的命令对象,然后将控制权传递到Command对象,由其完成后面的操作,这里面其实就是用到了Command模式。

           Front Controller的处理程序部分结构图

     

    Front Controller的命令部分结构图

    Handler类负责处理各个Web请求,并将确定正确的Command对象这一职责委派给CommandFactory类。当CommandFactory返回Command对象后,Handler将调用Command上的Execute方法来执行请求。具体的实现如下

    // Handler类
    public class Handler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            Command command = CommandFactory.Make(context.Request.Params);
            command.Execute(context);
        }
        public bool IsReusable
        {
            get
            {
                return true;
            }
        }
    }
    Command接口:
    /// <summary>
    /// Command
    /// </summary>
    public interface Command
    {
        void Execute(HttpContext context);
    }
    CommandFactory类:
    /// <summary>
    /// CommandFactory
    /// </summary>
    public class CommandFactory
    {
        public static Command Make(NameValueCollection parms)
        {
            string requestParm = parms["requestParm"];
            Command command = null;
            //根据输入参数得到不同的Command对象
            switch (requestParm)
            {
                case "1":
                    command = new FirstPortal();
                    break;
                case "2":
                    command = new SecondPortal();
                    break;
                default:
                    command = new FirstPortal();
                    break;
            }
            return command;
        }
    }
    RedirectCommand类:
    public abstract class RedirectCommand : Command
    {
        //获得Web.Config中定义的key和url键值对,UrlMap类详见下载包中的代码
        private UrlMap map = UrlMap.SoleInstance;
        protected abstract void OnExecute(HttpContext context);
        public void Execute(HttpContext context)
        {
            OnExecute(context);
            //根据key和url键值对提交到具体处理的页面
    string url = String.Format("{0}?{1}", map.Map[context.Request.Url.AbsolutePath], context.Request.Url.Query);
            context.Server.Transfer(url);
        }
    }
    FirstPortal类:
    public class FirstPortal : RedirectCommand
    {
        protected override void OnExecute(HttpContext context)
        {
            //在输入参数中加入项portalId以便页面处理
            context.Items["portalId"] = "1";
        }
    }
    SecondPortal类:
    public class SecondPortal : RedirectCommand
    {
        protected override void OnExecute(HttpContext context)
        {
            context.Items["portalId"] = "2";
        }
    }
    View Code

    命令模式的适用场景

           在下面的情况下可以考虑使用命令模式:

    1  系统需要支持命令的撤销(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo方法吧命令所产生的效果撤销掉。命令对象还可以提供redo方法,以供客户端在需要时,再重新实现命令效果。

    2  系统需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命周期。意思为:原来请求的发出者可能已经不存在了,而命令对象本身可能仍是活动的。这时命令的接受者可以在本地,也可以在网络的另一个地址。命令对象可以串行地传送到接受者上去。

    3  如果一个系统要将系统中所有的数据消息更新到日志里,以便在系统崩溃时,可以根据日志里读回所有数据的更新命令,重新调用方法来一条一条地执行这些命令,从而恢复系统在崩溃前所做的数据更新。

    4  系统需要使用命令模式作为“CallBack(回调)”在面向对象系统中的替代。Callback即是先将一个方法注册上,然后再以后调用该方法。

    命令模式的优缺点

    命令模式使得命令发出的一个和接收的一方实现低耦合,从而有以下的优点:

    A命令模式使得新的命令很容易被加入到系统里。

    B可以设计一个命令队列来实现对请求的Undo和Redo操作。

    C可以较容易地将命令写入日志。

    D可以把命令对象聚合在一起,合成为合成命令。合成命令式合成模式的应用。

    命令模式的缺点:

    使用命令模式可能会导致系统有过多的具体命令类。这会使得命令模式在这样的系统里变得不实际。


    迭代器模式

           迭代器模式是针对集合对象而生的,对于集合对象而言,必然涉及到集合元素的添加删除操作,也肯定支持遍历集合元素的操作,此时如果把遍历操作也放在集合对象的话,集合对象就承担太多的责任了,此时可以进行责任分离,把集合的遍历放在另一个对象中,这个对象就是迭代器对象。

    迭代器模式提供了一种方法来顺序访问一个集合对象中各个元素,而又无需暴露该对象的内部表示,这样既可以做到不暴露集合的内部结构,又可以让外部代码透明地访问集合内部元素。具体的结构图如下所示。

           从上图可以看出,迭代器模式由以下角色组成:

    迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口

    具体迭代器角色(Concrete Iteraror):具体迭代器角色实现了迭代器接口,并需要记录遍历中的当前位置。

    聚合角色(Aggregate):聚合角色负责定义获得迭代器角色的接口

    具体聚合角色(Concrete Aggregate):具体聚合角色实现聚合角色接口。

    迭代器代码示例

    // 抽象聚合类
    public interface IListCollection
    {
        Iterator GetIterator();
    }
    // 迭代器抽象类
    public interface Iterator
    {
        bool MoveNext();
        Object GetCurrent();
        void Next();
        void Reset();
    }
    // 具体聚合类
    public class ConcreteList : IListCollection
    {
        int[] collection;
        public ConcreteList()
        {
            collection = new int[] { 2, 4, 6, 8 };
        }
        public Iterator GetIterator()
        {
            return new ConcreteIterator(this);
        }
        public int Length
        {
            get { return collection.Length; }
        }
        public int GetElement(int index)
        {
            return collection[index];
        }
    }
    // 具体迭代器类
    public class ConcreteIterator : Iterator
    {
        // 迭代器要集合对象进行遍历操作,自然就需要引用集合对象
        private ConcreteList _list;
        private int _index;
        public ConcreteIterator(ConcreteList list)
        {
            _list = list;
            _index = 0;
        }
        public bool MoveNext()
        {
            if (_index < _list.Length)
            {
                return true;
            }
            return false;
        }
        public Object GetCurrent()
        {
            return _list.GetElement(_index);
        }
        public void Reset()
        {
            _index = 0;
        }
        public void Next()
        {
            if (_index < _list.Length)
            {
                _index++;
            }
        }
    }
    // 客户端
    class Program
    {
        static void Main(string[] args)
        {
            Iterator iterator;
            IListCollection list = new ConcreteList();
            iterator = list.GetIterator();
            while (iterator.MoveNext())
            {
                int i = (int)iterator.GetCurrent();
                Console.WriteLine(i.ToString());
                iterator.Next();
            }
            Console.Read();
        }
    }
    迭代器代码示例

    迭代器模式的应用

           在.NET下,迭代器模式中的聚集接口和迭代器接口都已经存在了,其中IEnumerator接口扮演的就是迭代器角色,IEnumberable接口则扮演的就是抽象聚集的角色,只有一个GetEnumerator()方法,关于这两个接口的定义可以自行参考MSDN。在.NET 1.0中,.NET 类库中很多集合都已经实现了迭代器模式,大家可以用反编译工具Reflector来查看下mscorlib程序集下的System.Collections命名空间下的类

    迭代器模式的适用场景

    1  系统需要访问一个聚合对象的内容而无需暴露它的内部表示。

    2  系统需要支持对聚合对象的多种遍历。

    3  系统需要为不同的聚合结构提供一个统一的接口。

    迭代器模式的优缺点

    优点:

    迭代器模式使得访问一个聚合对象的内容而无需暴露它的内部表示,即迭代抽象。

    迭代器模式为遍历不同的集合结构提供了一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作

    缺陷:

    迭代器模式在遍历的同时更改迭代器所在的集合结构会导致出现异常。所以使用foreach语句只能在对集合进行遍历,不能在遍历的同时更改集合中的元素。(有可能抛出错误,但是我没实际试过)


    观察者模式

    在现实生活中,处处可见观察者模式,例如,微信中的订阅号,订阅博客和QQ微博中关注好友,这些都属于观察者模式的应用。

    观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己的行为。具体结构图如下所示:

    从上面观察者模式的定义和生活中的例子,很容易知道,观察者模式中首先会存在两个对象,一个是观察者对象,另一个就是主题对象,然而,根据面向接口编程的原则,则自然就有抽象主题角色和抽象观察者角色。理清楚了观察者模式中涉及的角色后,接下来就要理清他们之间的关联了,要想主题对象状态发生改变时,能通知到所有观察者角色,则自然主题角色必须所有观察者的引用,这样才能在自己状态改变时,通知到所有观察者。

    抽象主题角色(Subject):抽象主题把所有观察者对象的引用保存在一个列表中,并提供增加和删除观察者对象的操作,抽象主题角色又叫做抽象被观察者角色,一般由抽象类或接口实现。

    抽象观察者角色(Observer):为所有具体观察者定义一个接口,在得到主题通知时更新自己,一般由抽象类或接口实现。

    具体主题角色(ConcreteSubject):实现抽象主题接口,具体主题角色又叫做具体被观察者角色

    具体观察者角色(ConcreteObserver):实现抽象观察者角色所要求的接口,以便使自身状态与主题的状态相协调。

    示例代码

    // 腾讯游戏订阅号类
    public class TenxunGame
    {
        // 订阅者对象
        public Subscriber Subscriber {get;set;} 
        public String Symbol {get; set;}
        public string Info {get ;set;}
        public void Update()
        {
            if (Subscriber != null)
            {
                // 调用订阅者对象来通知订阅者
                Subscriber.ReceiveAndPrintData(this);
            }
        }
    }
    // 订阅者类
    public class Subscriber
    {
        public string Name { get; set; }
        public Subscriber(string name)
        {
            this.Name = name;
        }
        public void ReceiveAndPrintData(TenxunGame txGame)
        {
            Console.WriteLine("Notified {0} of {1}'s" + " Info is: {2}", Name, txGame.Symbol, txGame.Info);
        }
    }
    
    // 客户端测试
    class Program
    {
        static void Main(string[] args)
        {
            // 实例化订阅者和订阅号对象
            Subscriber LearningHardSub = new Subscriber("LearningHard");
            TenxunGame txGame = new TenxunGame();
            txGame.Subscriber = LearningHardSub;
            txGame.Symbol = "TenXun Game";
            txGame.Info = "Have a new game published ....";
            txGame.Update();
            Console.ReadLine();
        }
    }
    示例代码

    UML类图

    观察者模式的应用

           .NET中,我们可以使用委托与事件来简化观察者模式的实现,上面的例子用事件和委托的实现如下代码所示:

     

    namespace ObserverInNET
    {
        class Program
        {
            // 委托充当订阅者接口类
            public delegate void NotifyEventHandler(object sender);
    
            // 抽象订阅号类
            public class TenXun
            {
                public NotifyEventHandler NotifyEvent;
    
                public string Symbol { get; set; }
                public string Info { get; set; }
                public TenXun(string symbol, string info)
                {
                    this.Symbol = symbol;
                    this.Info = info;
                }
    
                #region 新增对订阅号列表的维护操作
                public void AddObserver(NotifyEventHandler ob)
                {
                    NotifyEvent += ob;
                }
                public void RemoveObserver(NotifyEventHandler ob)
                {
                    NotifyEvent -= ob;
                }
    
                #endregion
    
                public void Update()
                {
                    if (NotifyEvent != null)
                    {
                        NotifyEvent(this);
                    }
                }
            }
    
            // 具体订阅号类
            public class TenXunGame : TenXun
            {
                public TenXunGame(string symbol, string info)
                    : base(symbol, info)
                {
                }
            }
    
            // 具体订阅者类
            public class Subscriber
            {
                public string Name { get; set; }
                public Subscriber(string name)
                {
                    this.Name = name;
                }
    
                public void ReceiveAndPrint(Object obj)
                {
                    TenXun tenxun = obj as TenXun;
    
                    if (tenxun != null)
                    {
                        Console.WriteLine("Notified {0} of {1}'s" + " Info is: {2}", Name, tenxun.Symbol, tenxun.Info);
                    }            
                }
            }
    
            static void Main(string[] args)
            {
                TenXun tenXun = new TenXunGame("TenXun Game", "Have a new game published ....");
                Subscriber lh =  new Subscriber("Learning Hard");
                Subscriber tom =  new Subscriber("Tom");
    
                // 添加订阅者
                tenXun.AddObserver(new NotifyEventHandler(lh.ReceiveAndPrint));
                tenXun.AddObserver(new NotifyEventHandler(tom.ReceiveAndPrint));
    
                tenXun.Update();
    
                Console.WriteLine("-----------------------------------");
                Console.WriteLine("移除Tom订阅者");
                tenXun.RemoveObserver(new NotifyEventHandler(tom.ReceiveAndPrint));
                tenXun.Update();
    
                Console.ReadLine();
            }
        }
    }
    用事件和委托的实现

           从上面代码可以看出,使用事件和委托实现的观察者模式中,减少了订阅者接口类的定义,此时,.NET中的委托正式充到订阅者接口类的角色。使用委托和事件,确实简化了观察者模式的实现,减少了一个IObserver接口的定义,上面代码的运行结果如下图所示:

    观察者模式的适用场景

    A当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中以使它们可以各自独立地改变和复用的情况下。从方面的这个词中可以想到,观察者模式肯定在AOP(面向方面编程)中有所体现,更多内容参考:Observern Pattern in AOP.

    B当对一个对象的改变需要同时改变其他对象,而又不知道具体有多少对象有待改变的情况下。

    C当一个对象必须通知其他对象,而又不能假定其他对象是谁的情况下。

    观察者模式的优缺点

           观察者模式有以下几个优点:

    A观察者模式实现了表示层和数据逻辑层的分离,并定义了稳定的更新消息传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层,即观察者。

    B观察者模式在被观察者和观察者之间建立了一个抽象的耦合,被观察者并不知道任何一个具体的观察者,只是保存着抽象观察者的列表,每个具体观察者都符合一个抽象观察者的接口。

    C观察者模式支持广播通信。被观察者会向所有的注册过的观察者发出通知。

           观察者也存在以下一些缺点:

    A如果一个被观察者有很多直接和间接的观察者时,将所有的观察者都通知到会花费很多时间。

    B虽然观察者模式可以随时使观察者知道所观察的对象发送了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎样发生变化的。

    C如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃,在使用观察者模式应特别注意这点。 


     

    中介者模式

           在现实生活中,有很多中介者模式的身影,例如QQ游戏平台,聊天室、QQ群和短信平台,这些都是中介者模式在现实生活中的应用。

    中介者模式,定义了一个中介对象来封装一系列对象之间的交互关系。中介者使各个对象之间不需要显式地相互引用,从而使耦合性降低,而且可以独立地改变它们之间的交互行为。具体的结构图如下所示:

     

     

    不使用中介者模式下,对象关系如下

     

    使用中介者模式

     

    示例代码

    // 抽象牌友类
    public abstract class AbstractCardPartner
    {
        public int MoneyCount { get; set; }
    
        public AbstractCardPartner()
        {
            MoneyCount = 0;
        }
    
        public abstract void ChangeCount(int Count, AbstractCardPartner other);
    }
    // 牌友A类
    public class ParterA : AbstractCardPartner
    {
        public override void ChangeCount(int Count, AbstractCardPartner other)
        {
            this.MoneyCount += Count;
            other.MoneyCount -= Count;
        }
    }
    // 牌友B类
    public class ParterB : AbstractCardPartner
    {
        public override void ChangeCount(int Count, AbstractCardPartner other)
        {
            this.MoneyCount += Count;
            other.MoneyCount -= Count;
        }
    }
    class Program
    {
        // A,B两个人打牌
        static void Main(string[] args)
        {
            AbstractCardPartner A = new ParterA();
            A.MoneyCount = 20;
            AbstractCardPartner B = new ParterB();
            B.MoneyCount = 20;
    
            // A 赢了则B的钱就减少
            A.ChangeCount(5, B);
            Console.WriteLine("A 现在的钱是:{0}", A.MoneyCount);// 应该是25
            Console.WriteLine("B 现在的钱是:{0}", B.MoneyCount); // 应该是15
            
            // B赢了A的钱也减少
            B.ChangeCount(10, A);
            Console.WriteLine("A 现在的钱是:{0}", A.MoneyCount); // 应该是15
            Console.WriteLine("B 现在的钱是:{0}", B.MoneyCount); // 应该是25
            Console.Read();        
        }
    }
    示例代码

           上面确实完美解决了上面场景中的问题,并且使用了抽象类使具体牌友A和牌友B都依赖于抽象类,从而降低了同事类之间的耦合度。但是这样的设计,如果其中牌友A发生变化时,此时就会影响到牌友B的状态,如果涉及的对象变多的话,这时候某一个牌友的变化将会影响到其他所有相关联的牌友状态。例如牌友A算错了钱,这时候牌友A和牌友B的钱数都不正确了,如果是多个人打牌的话,影响的对象就会更多。这时候就会思考——能不能把算钱的任务交给程序或者算数好的人去计算呢,这时候就有了我们QQ游戏中的欢乐斗地主等牌类游戏了。所以上面的设计,我们还是有进一步完善的方案的,即加入一个中介者对象来协调各个对象之间的关联,这也就是中介者模式的应用了,具体完善后的实现代码如下所示:

    namespace MediatorPattern
    {
        // 抽象牌友类
        public abstract class AbstractCardPartner
        {
            public int MoneyCount { get; set; }
            public AbstractCardPartner()
            {
                MoneyCount = 0;
            }
            public abstract void ChangeCount(int Count, AbstractMediator mediator);
        }
    
        // 牌友A类
        public class ParterA : AbstractCardPartner
        {
            // 依赖与抽象中介者对象
            public override void ChangeCount(int Count, AbstractMediator mediator)
            {
                mediator.AWin(Count);
            }
        }
        // 牌友B类
        public class ParterB : AbstractCardPartner
        {
            // 依赖与抽象中介者对象
            public override void ChangeCount(int Count, AbstractMediator mediator)
            {
                mediator.BWin(Count);
            }
        }
        // 抽象中介者类
        public abstract class AbstractMediator
        {
            protected AbstractCardPartner A;
            protected AbstractCardPartner B;
            public AbstractMediator(AbstractCardPartner a, AbstractCardPartner b)
            {
                A = a;
                B = b;
            }
            public abstract void AWin(int count);
            public abstract void BWin(int count);
        }
        // 具体中介者类
        public class MediatorPater : AbstractMediator
        {
            public MediatorPater(AbstractCardPartner a, AbstractCardPartner b)
                : base(a, b)
            {
            }
            public override void AWin(int count)
            {
                A.MoneyCount += count;
                B.MoneyCount -= count;
            }
            public override void BWin(int count)
            {
                B.MoneyCount += count;
                A.MoneyCount -= count;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                AbstractCardPartner A = new ParterA();
                AbstractCardPartner B = new ParterB();
                // 初始钱
                A.MoneyCount = 20;
                B.MoneyCount = 20;
    
                AbstractMediator mediator = new MediatorPater(A, B);
    
                // A赢了
                A.ChangeCount(5, mediator);
                Console.WriteLine("A 现在的钱是:{0}", A.MoneyCount);// 应该是25
                Console.WriteLine("B 现在的钱是:{0}", B.MoneyCount); // 应该是15
    
                // B 赢了
                B.ChangeCount(10, mediator);
                Console.WriteLine("A 现在的钱是:{0}", A.MoneyCount);// 应该是15
                Console.WriteLine("B 现在的钱是:{0}", B.MoneyCount); // 应该是25
                Console.Read();
            }
        }
    }
    完善后的实现

    中介者模式的适用场景

           一般在以下情况下可以考虑使用中介者模式:

    1  一组定义良好的对象,现在要进行复杂的相互通信。

    2  想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。

    中介者模式的优缺点

    中介者模式具有以下几点优点:

    A  简化了对象之间的关系,将系统的各个对象之间的相互关系进行封装,将各个同事类解耦,使得系统变为松耦合。

    B  提供系统的灵活性,使得各个同事对象独立而易于复用。

    然而,中介者模式也存在对应的缺点:

    A  中介者模式中,中介者角色承担了较多的责任,所以一旦这个中介者对象出现了问题,整个系统将会受到重大的影响。例如,QQ游戏中计算欢乐豆的程序出错了,这样会造成重大的影响。

    B  新增加一个同事类时,不得不去修改抽象中介者类和具体中介者类,此时可以使用观察者模式和状态模式来解决这个问题。

     

     

  • 相关阅读:
    Python学习系列之类与对象(二十三)
    面向过程和面向对象的异同点
    js 数值精确运算使用math.js
    js实现复制 、剪切功能-clipboard.min.js 示例
    css div嵌套层中button的margin-top不起作用解决方法
    IPhone中H5页面用on绑定click无效的解决方法
    This is a good start.
    element之input输入搜索联想框
    vue + element-ui 国际化实现
    async/await 处理多个网络请求同步问题
  • 原文地址:https://www.cnblogs.com/qixinbo/p/9120324.html
Copyright © 2011-2022 走看看