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

一、引出模式

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

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

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

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

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

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

二、认识模式

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.命令模式的本质

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

 

 

posted @ 2013-12-17 19:43  烧点饭  阅读(688)  评论(0编辑  收藏  举报