zoukankan      html  css  js  c++  java
  • 命令模式(Command)

    命令模式(Command

     

    命令模式(Command[Action/Transaction]

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

    应用:用户操作日志、撤销恢复操作。

    模式结构

    心得

    命令对象的抽象接口(Command)提供的两个常见操作——执行和撤销,其他的命令对象要实现这个接口。命令模式使之上是将调用对象(Invoker)与被调用对象(ImageText)之间的耦合关系解除。真正调用对象操作的是具体实现的命令对象,它把具体操作封装在execute内部,并为之实现了逆向的操作(如果可以的话)。而原先的调用者(invoker)只需要执行命令对象(Command)的executeunExecute操作即可,而并不知道它操作了什么样的具体对象。使用Command将对象间的调用耦合关系解除,同时也获得了用户操作的历史信息,把这些信息记录在一个队列内部,通过顺序访问队列重复执行unExecuteexcute操作即可完成撤销和恢复的功能。原则上撤销和执行的操作是相互抵消的,但是有时这并不能精确保证,为了解决类似问题可以使用备忘录模式精确记录对象状态。另外,在命令对象继承层次中引入组合模式可以实现宏命令的功能。

    举例

    为了方便理解,我们把调用者(invoker)看作GUI的控件对象,用户操作空间要改变图片的大小的时候会通过多态性质调用ResizeCommandexecute操作,而该execute会执行Imageresize操作。而用户要想撤销该操作,只需要访问命令队列,执行该对象的unExecute操作即可。具体的C++实现代码如下:

    //具体用户操作的对象
    class Image
    {
    public:
        void resize()
        {
            cout<<"改变图片大小"<<endl;
        }
    };
    class Text
    {
    public:
        void changeColor()
        {
            cout<<"改变文字颜色"<<endl;
        }
    };
    //命令
    class Command
    {
    public:
        virtual void execute()=0;
        virtual void unExecute()=0;
        virtual ~Command(){};
    };
    class ResizeCommand:public Command
    {
        Image img;
    public:
        virtual void execute()
        {
            img.resize();
        }
        virtual void unExecute()
        {
            cout<<"恢复图片大小"<<endl;
        }
    };
    class ColorCommand:public Command
    {
        Text txt;
    public:
        virtual void execute()
        {
            txt.changeColor();
        }
        virtual void unExecute()
        {
            cout<<"恢复文字颜色"<<endl;
        }
    };
    //命令队列
    class CmdList
    {
        static const unsigned int maxLen=20;
        vector<Command*>cmds;
        int curPos;//执行点——记录卡刚执行命令在队列的位置
    public:
        CmdList()
        {
            curPos=-1;
        }
        void storeCmd(Command*cmd)
        {
            //从执行点往后的元素先删除
            int times=cmds.size()-curPos-1;//执行点后边元素个数
            while(times--)
            {
                delete cmds.back();//清除内存
                cmds.pop_back();
            }
            //压入新命令
            cmds.push_back(cmd);
            if(cmds.size()>maxLen)//超过了一个元素,删除第一个
            {
                cmds.erase(cmds.begin());
            }
            curPos=cmds.size()-1;//插入新的元素即刚执行过,记录位置
        }
        void unDo()//撤销
        {
            if(curPos>=0)//有效位置
            {
                cmds[curPos--]->unExecute();
            }
        }
        void reDo()//恢复
        {
            if(curPos<(int)cmds.size()-1)//后边有节点才能恢复
            {
                cmds[++curPos]->execute();
            }
        }
        ~CmdList()
        {
            for(vector<Command*>::iterator it=cmds.begin();it!=cmds.end();++it)
            {
                delete *it;
            }
            cmds.clear();
        }
    };
    //调用者
    class Invoker
    {
    public:
        void operation(Command*cmd)
        {
            cmd->execute();
        }
    };

    而用户使用该模式需要的是构造合适的命令对象,发送到调用者那里就可以了。在命令成功执行后需要将该命令对象存储到命令队列去,这里根据实际需求决定存储命令的调用位置。上述代码中顺便实现了队列的撤销和恢复操作,其关键在于维护一个执行点curPos,记录当前执行过的命令对象所在的位置,当然这些对用户是不可见的。

    参考文章http://www.tracefact.net/Design-Pattern/Command.aspx

  • 相关阅读:
    NDBCLuster && MySQL Cluster 转
    Publisher FAQ | Readability: Enjoy Reading. Support Writing.
    Eucalyptus Cloud Computing Software
    Oracle Coherence
    hbase note
    使用python代码实现三叉搜索树高效率”自动输入提示”功能
    Facebook 的系统架构 肇凯博客
    CentOS Linux 环境 Rails 安装记录
    linux socket网络编程进阶推荐阅读开源项目转
    HandlerSocket
  • 原文地址:https://www.cnblogs.com/fanzhidongyzby/p/2818796.html
Copyright © 2011-2022 走看看