• 设计模式(五):命令模式


    一、定义

     将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作.

    我觉得可以这样:将一个请求封装为一个对象,对请求排队或记录请求日志,以及支持可撤销的操作;从而使你可用不同的请求对客户进行参数化;

    二、实例:一个遥控面板,两台小家电。一个面板控制两台小家电

    2.1  不用命令模式实现

    命令发起者:一块遥控器

    命令接收者:一堆小家电

    小家电接口:

     public interface ISmallAppliance
        {
            int Appid { get; set; }
            void Open();
            void Off();
        }

     电器:

    public class Purifier : ISmallAppliance
        {
            public int Appid { get; set; }
            public Purifier(int appid)
            {
                Appid = appid;
                Console.WriteLine("净化器: {0}", Appid);
            }
            public void Open()
            {
                Console.Write(" 启动【净化器】成功.");
            }
            public void Off()
            {
                Console.Write(" 关闭【净化器】成功.");
            }
        }
    
        public class Kettle : ISmallAppliance
        {
            public int Appid { get; set; }
            public Kettle(int appid)
            {
                Appid = appid;
                Console.WriteLine("电水壶: {0}", Appid);
            }
    
            public void Open()
            {
                Console.Write(" 启动【电水壶】成功.");
            }
            public void Off()
            {
                Console.Write(" 关闭【电水壶】成功.");
            }
        }

    遥控面板:

     public class CommandPannel
        {
            List<ISmallAppliance> apps = new List<ISmallAppliance>();        
            public void Sign(ISmallAppliance _sa)
            {
                if (!apps.Contains(_sa))
                    apps.Add(_sa);
            }
            public void OpenCommand(int appid)
            {
                ISmallAppliance app = apps.Where(q => q.Appid == appid).FirstOrDefault();
                app.Open();
            }
            public void OffCommand(int appid)
            {
                ISmallAppliance app = apps.Where(q => q.Appid == appid).FirstOrDefault();
                app.Off();
            }
        }

    客户端:

                //客厅一台电水壶
                Command.ISmallAppliance kettle_1 = new Command.Kettle(1);
                //厨房一台电水壶
                Command.ISmallAppliance kettle_2 = new Command.Kettle(2);
                //卧室一台净化器
                Command.ISmallAppliance purifier_1 = new Command.Purifier(100);
    
                List<Command.ISmallAppliance> smallAppliances = new List<Command.ISmallAppliance>();
                smallAppliances.Add(kettle_1);
                smallAppliances.Add(kettle_2);
                smallAppliances.Add(purifier_1);
    
                //遥控器
                Command.CommandPannel cp = new Command.CommandPannel(smallAppliances);
                //打开:kettle_1
                cp.OpenCommand(kettle_1.Appid);
                //关闭:purifier_1
                cp.OffCommand(purifier_100.Appid);

    由上可知:

    命令的发出者—遥控面板 和 命令的接收者—注册的各个小家电.

    命令发出者和命令接收者是紧耦合的:关闭和打开需要传递具体的电器ID。

    ---------------------------------------------------割----------------------------------------------------

    2.2、命令模式实现

     重温一下定义,其实每个设计模式的定义很重要,很多都限定了使用场景。

     定义:将一个请求封装为一个对象,对请求排队或记录请求日志,以及支持可撤销的操作;从而使你可用不同的请求对客户进行参数化;

    我们将定义分三个层次解读:

    2.2.1  将一个请求封装为一个对象. 

    即:我们将命令抽象,单独拿出来封装为对象。

    也就是 命令模式 三大要素之一:  Command  (命令载体)

    原来:遥控器有开按钮和关闭按钮。

     public class CommandPannel
        {
            List<ISmallAppliance> apps = new List<ISmallAppliance>();        
            public void Sign(ISmallAppliance _sa)
            {
                if (!apps.Contains(_sa))
                    apps.Add(_sa);
            }
            public void OpenCommand(int appid)
            {
                ISmallAppliance app = apps.Where(q => q.Appid == appid).FirstOrDefault();
                app.Open();
            }
            public void OffCommand(int appid)
            {
                ISmallAppliance app = apps.Where(q => q.Appid == appid).FirstOrDefault();
                app.Off();
            }
        }

    现在:我们需要进行抽象,将命令抽象出来,像这样 :当然每个命令指定了执行者 protected SmallAppliance sa;这个合乎实际,每个命令肯定针对具体的电器。

     abstract public class Commands
        {
            protected SmallAppliance sa;
            public Commands(SmallAppliance _sa)
            {
                sa = _sa;
            }
            abstract public void Do();
        }
        public class OpenCommand : Commands
        {
            public OpenCommand(SmallAppliance _sa) : base(_sa)
            {
            }
            override public void Do()
            {
                sa.Open();
            }
        }
        public class OffCommand : Commands
        {
            public OffCommand(SmallAppliance _sa) : base(_sa)
            {
            }
            override public void Do()
            {
                sa.Off();
            }
        }

    2.2.2  对请求排队或记录请求日志,以及支持可撤销的操作

    这是说的谁?遥控器 — 命令模式 三大要素之一:Invoker(请求处理者) 

    不过现在的遥控器,要拥有下面三个功能:请求排队、记录日志、可撤销

    public class Pannel
        {
            List<Commands> cmds = new List<Commands>();
            public Pannel()
            {
                Console.WriteLine("遥控器.");
            }
            public void Send(Commands cmd)
            {
                //请求排队
                cmds.Add(cmd);
                //记录日志
                Console.WriteLine("发送命令时间 : {0}", DateTime.Now);
            }
            public void CancleCmd(Commands cmd)
            {
                //可撤销           
                cmds.Remove(cmd);
                //记录日志
                Console.WriteLine("取消命令时间 : {0}", DateTime.Now);
            }
    
            public void Execute()
            {
                if (cmds != null && cmds.Count > 0)
                {
                    foreach (var cmd in cmds)
                    {
                        cmd.Do();
                    }
                }
            }
        }

    这就是我们改造之后的遥控器,简单粗暴的直接执行命令,不管谁的。来者不拒。

    2.2.3 命令模式 三大要素之一:Receiver(接收者)

      abstract public class SmallAppliance
        {
            abstract public int ID { get; set; }
            abstract public void Open();
            abstract public void Off();
        }
        public class Purifier : SmallAppliance
        {
            public Purifier(int id)
            {
                ID = id;
                Console.WriteLine("净化器: {0}", ID);
            }
            override public int ID { get; set; }
            override public void Open()
            {
                Console.Write(" 启动【净化器{0}】成功.", ID);
            }
            override public void Off()
            {
                Console.Write(" 关闭【净化器{0}】成功.", ID);
            }
        }
        public class Kettle : SmallAppliance
        {
    
            public Kettle(int id)
            {
                ID = id;
                Console.WriteLine("电水壶: {0}", ID);
            }
            override public int ID { get; set; }
            override public void Open()
            {
                Console.Write(" 启动【电水壶{0}】成功.", ID);
            }
            override public void Off()
            {
                Console.Write(" 关闭【电水壶{0}】成功.", ID);
            }
        }

    2.2.4  从而使你可用不同的请求对客户进行参数化

    看看原来的客户端:打开和关闭没有好的办法进行统一传递参数,需要传递具体的电器的具体ID号

                //客厅一台电水壶
                Command.ISmallAppliance kettle_1 = new Command.Kettle(1);
                //厨房一台电水壶
                Command.ISmallAppliance kettle_2 = new Command.Kettle(2);
                //卧室一台净化器
                Command.ISmallAppliance purifier_1 = new Command.Purifier(100);
    
                List<Command.ISmallAppliance> smallAppliances = new List<Command.ISmallAppliance>();
                smallAppliances.Add(kettle_1);
                smallAppliances.Add(kettle_2);
                smallAppliances.Add(purifier_1);
    
                //遥控器
                Command.CommandPannel cp = new Command.CommandPannel(smallAppliances);
                //打开:kettle_1
                cp.OpenCommand(kettle_1.Appid);
                //关闭:purifier_1
                cp.OffCommand(purifier_100.Appid);

    再来看看现在:

                //Receiver(接收者):客厅一台电水壶
                Command.SmallAppliance kettle_1 = new Command.Kettle(1);
                //Receiver(接收者):厨房一台电水壶
                Command.SmallAppliance kettle_2 = new Command.Kettle(2);
                //Receiver(接收者):卧室一台净化器
                Command.SmallAppliance purifier_100 = new Command.Purifier(100);
    
                //Invoker(处理者)—遥控器
                Command.Pannel cp = new Command.Pannel();
                Command.OpenCommand cmd_open_k1 = new Command.OpenCommand(kettle_1);
                Command.OpenCommand cmd_open_p100 = new Command.OpenCommand(kettle_1);
                Command.OffCommand cmd_off_k1 = new Command.OffCommand(kettle_1);          
                Command.OffCommand cmd_off_p100 = new Command.OffCommand(kettle_1);            
                //发送命令 
                cp.Send(cmd_open_k1);
                cp.Send(cmd_open_p100);
                cp.Send(cmd_off_k1);
                cp.CancleCmd(cmd_off_k1);
                cp.Send(cmd_off_p100);
                //执行命令
                cp.Execute();

     

    三、总结

    1、Recevier(接收者):最简单的接收者还是那些小家电,还是一样的味道.

    2、Command(抽象命令): 将遥控器的的命令或者请求,抽象为Command(命令).【这是思想重要的转变】

    3、Invoker(处理者):然后改造遥控器使之拥有三个核心功能:请求排队、记录日志、可撤销.【操作核心】

    4、Client(客户端):就是我,我家里买了两台电水壶外加一台净化器,然后我快速的按着遥控器,嘀嘀,嘀嘀,嘀嘀,嘀嘀,嘀,TMD错了,撤销~~。然后喝水吃饭打豆豆

  • 相关阅读:
    ASP.NET Core 中间件(Middleware)详解
    .NET Core 使用RSA算法 加密/解密/签名/验证签名
    【Other】希腊诸神大全-中英文名称
    【架构】分布式追踪系统设计与实现
    【架构】SpringCloud 注册中心、负载均衡、熔断器、调用监控、API网关示例
    【SpringCloud】Netflix源码解析之Ribbon:负载均衡策略的定义和实现
    【Docker】基于docker+etcd+confd + haproxy构建高可用、自发现的web服务
    【架构】Kubernetes和Spring Cloud哪个部署微服务更好?
    【Linux】Linux中 “there are stopped jobs”问题的解决方案
    【架构】分布式系统雪崩效应处理方案
  • 原文地址:https://www.cnblogs.com/sunchong/p/5106513.html
Copyright © 2020-2023  润新知