• 设计模式之命令模式(十三)


    一、引出模式

    在前面的模式中,我们已经组装好了一台电脑,现在要做的是开机。是的,开机!对用户们来说开机只不过是按个电源按钮,跟喝水一样简单!但对于我们搞技术的,就不一样了,可这其中又发生了什么不为人知的事呢?自己百度去!

    我们先简单将以下流程,不做深入讲解。

    首先加载电源,然后设备自检,接下来装在操作系统,最后电脑就启动了。可是谁来完成这些过程?如何完成的呢?

    总不能让用户做这些吧,其实真正完成这些功能的是主板。那客户和主板又是怎么联系的呢?现实中,使用连接线将按钮连接到主板上,这样当用户按下按钮时,就相当与发命令给主板,让主板去完成后续工作。

    想想,在这里有没有什么问题?

    我们把这种情形放到软件开发中看看。客户端只是想要发出命令,不关心命令的执行者是谁,也不关心执行者是怎么完成的,有时同一个请求可能需要执行不同的操作,那怎么办?

    二、认识模式

    1.模式定义

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

    2.解决思路

    我们来试着用命令模式解决上述开机的过程。

    当客户按下按钮时,按钮本身并不知道如何处理,于是我们通过连接线,将按钮和主板连接起来,让主板去完成真正启动机器的功能。

    在这里,我们通过引入按钮和连接线,来让发出命令的客户和命令的真正实现者——主板完全解耦,客户操作的始终是按钮,按钮后面的事客户就不管了,因为客户只知道只要按下按钮就能开机了,中间做了什么客户是不关心的,客户只求结果,不问过程。

    在命令模式中,会定义一个命令的接口,用来约束所有的命令对象,然后提供具体的命令实现,每个命令实现对象是对客户端某个请求的封装,对应于机箱上的按钮,一个机箱可以有很多按钮,也就相当于有很多个具体的命令实现对象。

    在命令模式中,命令对象是不知道如何处理命令的,他会转调命令接受者对象来真正执行命令。就像刚才例子中,按钮是不知道如何处理的,按钮是吧这个请求转发给主板,主板来执行,这个主板就相当于命令模式汇总的接受者。

    在命令模式中,命令对象和接收者的关系并不是与生俱来的,需要有一个装配者对两者进行关联,命令模式中的Client对象可以实现这样的功能,在电脑中,有了按钮,有了主板,那还需要有根连接线将按钮和主板连接起来才行,这根连接线就充当着Client对象的角色。

    命令模式中,还会提供一个Invoker对象来持有命令对象。比如,机箱上会有多个按钮,这个机箱就相当于Invoker对象,这样我们客户就可以通过Invoker也就是机箱来按下按钮来执行相应的命令。

    3.模式结构原型

    Command:定义命令的接口,声明执行的方法。

    ConreteCommand:命令接口实现对象,是“虚”的实现;通常会持有接受者,并调用接受者的功能来完成命令要执行的操作。

    Receiver:接受者,真正执行命令的对象。任何类都能成为接受者,只要它能够实现命令要求实现的相应的功能。

    Invoker:要求命令对象执行请求,通常会持有命苦对象,可以持有很多命令对象。这个是客户端真正触发命令并要求执行相应操作的定法,也就是说这才是使用命令对象的入口。

    Client:创建具体的命令对象,并且设置命令对象的接受者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接受者,可以将它称为装配者,真正使用命令的客户端是从Invoker来触发执行的。

    4.模式原型示例代码

    class Program
        {
            static void Main(string[] args)
            {
                Client client = new Client();
                client.Run();
                Console.ReadKey();
            }
        }
    
        /// <summary>
        /// 示意,负责创建命令对象,并设定它的接受者
        /// </summary>
        public class Client
        {
            public void Run()
            {
                Receiver receiver = new Receiver();
                //创建命令对象,设定它的接收者
                Command command = new ConcreteCommand(receiver);
                //创建Invoker,把命令对象设置进去
                Invoker invoker = new Invoker(command);
                invoker.RunCommand();
            }
        }
    
        /// <summary>
        /// 命令接口,声明执行的操作
        /// </summary>
        public interface Command
        {
            /// <summary>
            /// 执行命令对应的操作
            /// </summary>
            void Execute();
        }
    
        /// <summary>
        /// 具体的命令实现对象
        /// </summary>
        public class ConcreteCommand : Command
        {
            /// <summary>
            /// 持有相应的接受者对象
            /// </summary>
            private Receiver receiver = null;
    
            /// <summary>
            /// 示意,命令对象可以有自己的状态
            /// </summary>
            private string state = null;
    
            /// <summary>
            /// 构造方法,传入相应的接受者对象
            /// </summary>
            /// <param name="receiver">相应的接受者对象</param>
            public ConcreteCommand(Receiver receiver)
            {
                this.receiver = receiver;
            }
    
            public void Execute()
            {
                //通常会转调接受者对象的相应方法,让接受者来真正执行功能
                receiver.Action();
            }
        }
    
        /// <summary>
        /// 接收者对象
        /// </summary>
        public class Receiver
        {
            /// <summary>
            ///  示意方法,真正执行命令相应的操作
            /// </summary>
            public void Action()
            {
                Console.WriteLine("命令执行了");
            }
        }
    
        /// <summary>
        /// 调用者(机箱)
        /// </summary>
        public class Invoker
        {
            /// <summary>
            /// 持有命令对象
            /// </summary>
            private Command command;
    
            /// <summary>
            /// 设置调用者持有的命令对象
            /// </summary>
            /// <param name="command">命令对象</param>
            public Invoker(Command command)
            {
                this.command = command;
            }
    
            /// <summary>
            /// 示意方法,要求命令执行请求
            /// </summary>
            public void RunCommand()
            {
                command.Execute();
            }
        }

    5.电脑开机示例示例代码

    class Program
        {
            static void Main(string[] args)
            {
                //把命令和真正的实现组合起来,相当于在组装机器,
                MainBoardApi mainBoardApi=new GigaMainBoard();
    
                //把机箱上按钮的连接线插接到主板上。
                Command command=new OpenCommand(mainBoardApi);
    
                //真正的客户端测试
    
                //为机箱上的按钮设置对应的命令,让按钮知道该干什么
                Box box=new Box();
                box.SetOpenCommand(command);
    
                //然后模拟按下机箱上的按钮
                box.OpenButtonPressed();
    
                Console.Read();
            }
        }
    
        /// <summary>
        /// 命令接口,声明执行的操作
        /// </summary>
        public interface Command
        {
            /// <summary>
            /// 执行命令对应的操作
            /// </summary>
            void Execute();
        }
    
        /// <summary>
        /// 持有开机命令的真正实现,通过调用接收者的方法来实现命令
        /// </summary>
        public class OpenCommand : Command
        {
            /// <summary>
            /// 持有真正实现命令的接收者——主板对象
            /// </summary>
            private MainBoardApi mainBoard = null;
    
            /// <summary>
            /// 构造方法,传入主板对象
            /// </summary>
            /// <param name="mainBoard">主板对象</param>
            public OpenCommand(MainBoardApi mainBoard)
            {
                this.mainBoard = mainBoard;
            }
    
            public void Execute()
            {
                //对于命令对象,根本不知道如何开机,会转调主板对象
                //让主板去完成开机的功能
                this.mainBoard.Open();
            }
        }
    
        /// <summary>
        /// 主板的接口
        /// </summary>
        public interface MainBoardApi
        {
    
            /// <summary>
            /// 主板具有能开机的功能
            /// </summary>
            void Open();
        }
    
        /// <summary>
        /// 技嘉主板类,开机命令的真正实现者,在Command模式中充当Receiver
        /// </summary>
        public class GigaMainBoard : MainBoardApi
        {
    
            /// <summary>
            /// 真正的开机命令的实现
            /// </summary>
            public void Open()
            {
                Console.WriteLine("技嘉主板现在正在开机,请等候");
                Console.WriteLine("接通电源......");
                Console.WriteLine("设备检查......");
                Console.WriteLine("装载系统......");
                Console.WriteLine("机器正常运转起来......");
                Console.WriteLine("机器已经正常打开,请等候");
            }
        }
    
        /// <summary>
        /// 微星主板类,开机命令的真正实现者,在Command模式中充当Receiver
        /// </summary>
        public class MsiMainBoard : MainBoardApi
        {
            /// <summary>
            /// 真正的开机命令的实现
            /// </summary>
            public void Open()
            {
                Console.WriteLine("微星主板现在正在开机,请等候");
                Console.WriteLine("接通电源......");
                Console.WriteLine("设备检查......");
                Console.WriteLine("装载系统......");
                Console.WriteLine("机器正常运转起来......");
                Console.WriteLine("机器已经正常打开,请等候");
            }
        }
    
    
        /// <summary>
        /// 机箱对象,本身有按钮,持有按钮对应的命令对象
        /// </summary>
        public class Box
        {
           /// <summary>
            /// 开机命令对象
           /// </summary>
            private Command openCommand;
    
            /// <summary>
            /// 设置开机命令对象
            /// </summary>
            /// <param name="command">开机命令对象</param>
            public void SetOpenCommand(Command command)
            {
                this.openCommand = command;
            }
            
            /// <summary>
            /// 提供给客户使用,接受并相应用户请求,相当于按钮被按下触发的方法
            /// </summary>
            public void OpenButtonPressed()
            {
                //按下按钮,执行命令
                openCommand.Execute();
            }
        }

    三、理解模式

    1.命令模式的关键

    命令模式的关键之处就是把请求封装称为对象,也就是命令对象,并定义统一的执行操作的接口,这个命令对象被存储、转发、记录、处理、撤销等,整个命令模式都是围绕这个对象进行的。

    2.命令模式的组装和调用

    命令模式中经常会有一个命令的装配者,用它来维护命令的“虚”实现和真实实现之间的关系。如果是超级智能的命令,也就是说命令对自己完全实现好了,不需要接受者,那就是命令模式的退化,不需要接受者,自然也不需要装配者。

    实际开发中,ClientInvoker是可以融合在一起的,有客户在使用命令模式的时候,先进行命令对象和接受者的组装,组装完成后,就可以调用命令执行请求。

    3.命令的接受者

    接受者是可以是任意的类,只要这个对象知道如何真正的执行命令,执行时是从Command的实现类里面转调过来的。

    一个接受者对象可以处理多个命令对象,接受者和命令之间没有约定的对象关系。

    4.智能命令

    在标准的命令模式中,命令的实现类是没有真正实现命令要求的功能的,真正执行命令的是接受者。

    如果命令的实现对象比较智能,自己就能实现命令要求的功能,就不需要调用接受者,这种情况称为智能命令。

    5.发起请求的对象和真正处理的对象是解耦的

    请求有谁来处理?如何处理?发起请求的对象是不知道的,也就是发情请求的对象和真正实现的对象是解耦的。

    6.参数化配置

    所谓的命令模式的参数化配置,指的是:可以用不同的命令对象,去参数化配置客户端的请求。

    如前面的表述,按下按钮你是不知道是开机,关机还是重启的,那就要看参数化配置是哪一个具体的按钮对象。

    示例代码:

    class Program
        {
            static void Main(string[] args)
            {
                //把命令和真正的实现组合起来,相当于在组装机器,
                MainBoardApi mainBoardApi = new GigaMainBoard();
    
                //创建开机命令
                Command command = new OpenCommand(mainBoardApi);
    
                //创建重启机器的命令
                ResetCommand resetCommand = new ResetCommand(mainBoardApi);
    
                //真正的客户端测试
    
                //为机箱上的按钮设置对应的命令,让按钮知道该干什么
                Box box = new Box();
    
                //先正确配置,就是开机按钮对开机命令,重启按钮对重启命令
                box.SetOpenCommand(command);
                box.SetResetCommand(resetCommand);
    
                //然后模拟按下机箱上的按钮
                Console.WriteLine("正确配置下------------------------->");
                Console.WriteLine(">>>按下开机按钮:>>>");
    
                box.OpenButtonPressed();
                Console.WriteLine(">>>按下重启按钮:>>>");
                box.ResetButtonPressed();
                Console.Read();
            }
        }
    
        /// <summary>
        /// 命令接口,声明执行的操作
        /// </summary>
        public interface Command
        {
            /// <summary>
            /// 执行命令对应的操作
            /// </summary>
            void Execute();
        }
    
        /// <summary>
        /// 持有开机命令的真正实现,通过调用接收者的方法来实现命令
        /// </summary>
        public class OpenCommand : Command
        {
            /// <summary>
            /// 持有真正实现命令的接收者——主板对象
            /// </summary>
            private MainBoardApi mainBoard = null;
    
            /// <summary>
            /// 构造方法,传入主板对象
            /// </summary>
            /// <param name="mainBoard">主板对象</param>
            public OpenCommand(MainBoardApi mainBoard)
            {
                this.mainBoard = mainBoard;
            }
    
            public void Execute()
            {
                //对于命令对象,根本不知道如何开机,会转调主板对象
                //让主板去完成开机的功能
                this.mainBoard.Open();
            }
        }
    
        /// <summary>
        /// 重启机器命令的实现,实现Command接口,
        /// 持有重启机器命令的真正实现,通过调用接收者的方法来实现命令
        /// </summary>
        public class ResetCommand : Command
        {
    
            /// <summary>
            /// 持有真正实现命令的接收者——主板对象
            /// </summary>
            private MainBoardApi mainBoard = null;
    
            /// <summary>
            /// 构造方法,传入主板对象
            /// </summary>
            /// <param name="mainBoard">主板对象</param>
            public ResetCommand(MainBoardApi mainBoard)
            {
                this.mainBoard = mainBoard;
            }
    
    
            public void Execute()
            {
                //对于命令对象,根本不知道如何重启机器,会转调主板对象
                //让主板去完成重启机器的功能
                this.mainBoard.Reset();
            }
        }
    
    
        /// <summary>
        /// 主板的接口
        /// </summary>
        public interface MainBoardApi
        {
    
            /// <summary>
            /// 主板具有能开机的功能
            /// </summary>
            void Open();
    
            /// <summary>
            /// 主板具有实现重启的功能
            /// </summary>
            void Reset();
        }
    
        /// <summary>
        /// 技嘉主板类,开机命令的真正实现者,在Command模式中充当Receiver
        /// </summary>
        public class GigaMainBoard : MainBoardApi
        {
    
            /// <summary>
            /// 真正的开机命令的实现
            /// </summary>
            public void Open()
            {
                Console.WriteLine("技嘉主板现在正在开机,请等候");
                Console.WriteLine("接通电源......");
                Console.WriteLine("设备检查......");
                Console.WriteLine("装载系统......");
                Console.WriteLine("机器正常运转起来......");
                Console.WriteLine("机器已经正常打开,请等候");
            }
    
            /// <summary>
            /// 真正的重新启动机器命令的实现
            /// </summary>
            public void Reset()
            {
                Console.WriteLine("微星主板现在正在重新启动机器,请等候");
                Console.WriteLine("机器已经正常打开,请等候");
            }
        }
    
        /// <summary>
        /// 微星主板类,开机命令的真正实现者,在Command模式中充当Receiver
        /// </summary>
        public class MsiMainBoard : MainBoardApi
        {
            /// <summary>
            /// 真正的开机命令的实现
            /// </summary>
            public void Open()
            {
                Console.WriteLine("微星主板现在正在开机,请等候");
                Console.WriteLine("接通电源......");
                Console.WriteLine("设备检查......");
                Console.WriteLine("装载系统......");
                Console.WriteLine("机器正常运转起来......");
                Console.WriteLine("机器已经正常打开,请等候");
            }
    
            /// <summary>
            /// 真正的重新启动机器命令的实现
            /// </summary>
            public void Reset()
            {
                Console.WriteLine("微星主板现在正在重新启动机器,请等候");
                Console.WriteLine("机器已经正常打开,请等候");
            }
        }
    
    
        /// <summary>
        /// 机箱对象,本身有按钮,持有按钮对应的命令对象
        /// </summary>
        public class Box
        {
            /// <summary>
            /// 开机命令对象
            /// </summary>
            private Command openCommand;
    
            /// <summary>
            /// 设置开机命令对象
            /// </summary>
            /// <param name="command">开机命令对象</param>
            public void SetOpenCommand(Command command)
            {
                this.openCommand = command;
            }
    
            /// <summary>
            /// 提供给客户使用,接受并相应用户请求,相当于按钮被按下触发的方法
            /// </summary>
            public void OpenButtonPressed()
            {
                //按下按钮,执行命令
                openCommand.Execute();
            }
    
            /// <summary>
            /// 重启机器命令对象
            /// </summary>
            private Command resetCommand;
    
            /// <summary>
            /// 设置重启机器命令对象
            /// </summary>
            /// <param name="command"></param>
            public void SetResetCommand(Command command)
            {
                this.resetCommand = command;
            }
    
            /// <summary>
            /// 提供给客户使用,接受并相应用户请求,相当于重启按钮被按下触发的方法
            /// </summary>
            public void ResetButtonPressed()
            {
                //按下按钮,执行命令
                resetCommand.Execute();
            }
        }

    7.可撤销的操作

    可撤销的操作意思是:放弃该操作,回到未执行操作前的状态。

    有两种基本的思路来实现可撤销的操作,一种是补偿式又称反操作式,比如被撤销的操作是+,那撤销的操作就是-

    另一种是存储恢复式,就是把操作前的状态记录下来,然后要撤销操作时直接恢复回去。

    在这里我们演示第一种可撤销操作,剩下一种等到备忘录模式时在讲。

    做一个计算机功能,只需要实现加减运算,还要让这个计算器支持可撤销的

    示例代码:

    class Program
        {
            static void Main(string[] args)
            {
    
                //1:组装命令和接收者
                //创建接收者
                OperationApi operation = new Operation();
    
                //创建命令对象,并组装命令和接收者
                AddCommand addCmd = new AddCommand(operation, 5);
                SubCommand substractCmd = new SubCommand(operation, 3);
    
                //2:把命令设置到持有者,就是计算器里面
                Calculator calculator = new Calculator();
                calculator.SetAddCommand(addCmd);
                calculator.SetSubCommand(substractCmd);
    
                //3:模拟按下按钮,测试一下
                calculator.AddPressed();
                Console.WriteLine("一次加法运算后的结果为:" + operation.GetResult());
                calculator.SubPressed();
                Console.WriteLine("一次减法运算后的结果为:" + operation.GetResult());
    
                //测试撤消
                calculator.UndoPressed();
                Console.WriteLine("撤销一次后的结果为:" + operation.GetResult());
                calculator.UndoPressed();
                Console.WriteLine("再撤销一次后的结果为:" + operation.GetResult());
    
                //测试恢复
                calculator.RedoPressed();
                Console.WriteLine("恢复操作一次后的结果为:" + operation.GetResult());
                calculator.RedoPressed();
                Console.WriteLine("再恢复操作一次后的结果为:" + operation.GetResult());
    
                Console.Read();
            }
        }
    
        /// <summary>
        ///  命令接口,声明执行的操作,支持可撤销操作
        /// </summary>
        public interface Command
        {
            /// <summary>
            /// 执行命令对应的操作
            /// </summary>
            void Execute();
    
            /// <summary>
            /// 执行撤销命令对应的操作
            /// </summary>
            void Undo();
        }
    
        /// <summary>
        /// 具体的加法命令实现对象
        /// </summary>
        public class AddCommand : Command
        {
            /// <summary>
            /// 持有具体执行计算的对象
            /// </summary>
            private OperationApi operationApi = null;
    
            /// <summary>
            /// 操作的数据,也就是要加上的数据
            /// </summary>
            private int num;
    
            /// <summary>
            /// 构造方法,传入具体执行计算的对象
            /// </summary>
            /// <param name="operationApi"></param>
            /// <param name="num"></param>
            public AddCommand(OperationApi operationApi, int num)
            {
                this.operationApi = operationApi;
                this.num = num;
            }
    
            public void Execute()
            {
                ////转调接收者去真正执行功能,这个命令是做加法
                operationApi.Add(num);
            }
    
            public void Undo()
            {
                //转调接收者去真正执行功能
                //命令本身是做加法,那么撤销的时候就是做减法了
                operationApi.Sub(num);
            }
        }
    
        /// <summary>
        /// 具体的减法命令实现对象
        /// </summary>
        public class SubCommand : Command
        {
            /// <summary>
            /// 持有具体执行计算的对象
            /// </summary>
            private OperationApi operationApi = null;
    
            /// <summary>
            /// 操作的数据,也就是要加上的数据
            /// </summary>
            private int num;
    
            /// <summary>
            /// 构造方法,传入具体执行计算的对象
            /// </summary>
            /// <param name="operationApi"></param>
            /// <param name="num"></param>
            public SubCommand(OperationApi operationApi, int num)
            {
                this.operationApi = operationApi;
                this.num = num;
            }
    
            public void Execute()
            {
                //转调接收者去真正执行功能,这个命令是做减法
                operationApi.Sub(num);
            }
    
            public void Undo()
            {
                //转调接收者去真正执行功能
                //命令本身是做减法,那么撤销的时候就是做加法了
                operationApi.Add(num);
            }
        }
    
        /// <summary>
        /// 操作运算的接口
        /// </summary>
        public interface OperationApi
        {
            /// <summary>
            /// 获取计算完成后的结果
            /// </summary>
            /// <returns></returns>
            int GetResult();
    
            /// <summary>
            /// 设置计算开始的初始值
            /// </summary>
            /// <param name="result"></param>
            void SetResult(int result);
    
            /// <summary>
            /// 执行加法
            /// </summary>
            /// <param name="num"></param>
            void Add(int num);
    
            /// <summary>
            /// 执行减法
            /// </summary>
            /// <param name="num"></param>
            void Sub(int num);
        }
    
        /// <summary>
        /// 运算类,真正实现加减法运算
        /// </summary>
        public class Operation : OperationApi
        {
            /// <summary>
            /// 记录运算的结果
            /// </summary>
            private int result;
    
            public int GetResult()
            {
                return result;
            }
    
            /// <summary>
            /// 设置值
            /// </summary>
            /// <param name="result"></param>
            public void SetResult(int result)
            {
                this.result = result;
            }
    
            /// <summary>
            /// 实现加法功能
            /// </summary>
            /// <param name="num"></param>
            public void Add(int num)
            {
                result += num;
            }
    
            /// <summary>
            /// 实现减法功能
            /// </summary>
            /// <param name="num"></param>
            public void Sub(int num)
            {
                result -= num;
            }
        }
    
        /// <summary>
        /// 计算器类,计算器上有加法按钮、减法按钮,还有撤销和恢复的按钮
        /// </summary>
        public class Calculator
        {
            /// <summary>
            /// 命令的操作的历史记录,在撤销时候用
            /// </summary>
            private List<Command> undoCmds = new List<Command>();
    
            /// <summary>
            /// 命令被撤销的历史记录,在恢复时候用
            /// </summary>
            private List<Command> redoCmds = new List<Command>();
    
            /// <summary>
            /// 持有执行加法的命令对象
            /// </summary>
            private Command addCommand = null;
    
            /// <summary>
            /// 持有执行减法的命令对象
            /// </summary>
            private Command subCommand = null;
    
            /// <summary>
            /// 设置执行加法的命令对象
            /// </summary>
            /// <param name="addCommand"></param>
            public void SetAddCommand(Command addCommand)
            {
                this.addCommand = addCommand;
            }
    
            /// <summary>
            /// 设置执行减法的命令对象
            /// </summary>
            /// <param name="subCommand"></param>
            public void SetSubCommand(Command subCommand)
            {
                this.subCommand = subCommand;
            }
    
            /// <summary>
            /// 加法按钮
            /// </summary>
            public void AddPressed()
            {
                this.addCommand.Execute();
                //把操作记录到历史记录里面
                undoCmds.Add(this.addCommand);
            }
    
            /// <summary>
            /// 减法按钮
            /// </summary>
            public void SubPressed()
            {
                this.subCommand.Execute();
                //把操作记录到历史记录里面
                undoCmds.Add(this.subCommand);
            }
    
            /// <summary>
            /// 撤销按钮
            /// </summary>
            public void UndoPressed()
            {
                if (this.undoCmds.Count > 0)
                {
                    //取出最后一个命令来撤销
                    Command cmd = this.undoCmds.Last();
                    cmd.Undo();
                    //如果还有恢复的功能,那就把这个命令记录到恢复的历史记录里面
                    this.redoCmds.Add(cmd);
                    //然后把最后一个命令删除掉,
                    this.undoCmds.Remove(cmd);
                }
                else
                {
                    Console.WriteLine("很抱歉,没有可撤销的命令");
                }
            }
    
            /// <summary>
            /// 恢复按钮
            /// </summary>
            public void RedoPressed()
            {
                if (this.redoCmds.Count > 0)
                {
                    //取出最后一个命令来重做
                    Command cmd = this.redoCmds.Last();
                    cmd.Execute();
                    //把这个命令记录到可撤销的历史记录里面
                    this.undoCmds.Add(cmd);
                    //然后把最后一个命令删除掉
                    this.redoCmds.Remove(cmd);
                }
                else
                {
                    Console.WriteLine("很抱歉,没有可恢复的命令");
                }
            }
        }

    8.宏命令

    宏命令就是包含多个命令的命令,是一个命令的组合。命令命令模式也是能实现的。

    9.队列请求

    所谓队列请求,就是对命令对象进行排队,组成工作队列,然后一次取出命令对象来执行。

    10.日志请求 

    日志请求,就是将请求的历史记录保存下来,一般是采用永久存储的方式。如果运行请求过程中,系统崩溃了,那么当系统再次运行时,就可以从保存的历史记录中获取日志请求,并重新执行命令。

    11.命令模式的优点

    更松散的耦合

    命令模式使得发起命令的对象——客户端,和命令的执行者对象完全解耦。

    更动态的控制

    命令模式将请求封装起来,可以动态地对它进行参数化、队列化和日志化等操作,使得系统更加灵活。

    更自然的复合命令

    命令模式中的命令对象能够很容易的组合成符合命令,如前面的宏命令。

    12.何时选用命令模式

    如果需要抽象出需要执行的动作,并参数化这些对象,可以使用命令模式。将这些需要执行的动作抽象成为命令,然后实现命令的参数化配置。

    如果需要在不同的时刻指定、排列和执行请求,可以选用命令模式。

    如果需要支持取消操作,可以选用命令模式,通过管理命令对象,很容易实现命令的恢复和重做功能。

    如果需要支持系统崩溃时,重启后能将系统的操作功能重新执行一遍,可以选用命令模式。

    在需要事务的系统中,可以选用命令模式。

    13.命令模式的本质

    命令模式的本质就是“封装请求”。命令模式的关键就是把请求封装称为命令对象,然后就可以对这个对象进行一系列的处理。

     

     

  • 相关阅读:
    蓝书3.6 割点与桥
    蓝书3.5 强连通分量
    蓝书3.4 差分约束系统
    蓝书3.3 SPFA算法的优化
    蓝书3.2 最短路
    蓝书3.1 最小生成树
    luogu 4630 [APIO2018] Duathlon 铁人两项
    Codeforces Round #124 (Div. 1) C. Paint Tree(极角排序)
    dutacm.club Water Problem(矩阵快速幂)
    dutacm.club 1094: 等差区间(RMQ区间最大、最小值,区间GCD)
  • 原文地址:https://www.cnblogs.com/zxj159/p/3479154.html
Copyright © 2020-2023  润新知