命令模式(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

posted @ 2012-12-14 23:53  Florian  阅读(1037)  评论(0编辑  收藏  举报