命令模式(Command)
命令模式(Command)
命令模式(Command)[Action/Transaction]
意图:将一个请求封装为一个对象,从而可用不同的请求对客户参数化。对请求排队或记录请求日志,以及支持可撤消的操作。
应用:用户操作日志、撤销恢复操作。
模式结构:
心得:
命令对象的抽象接口(Command)提供的两个常见操作——执行和撤销,其他的命令对象要实现这个接口。命令模式使之上是将调用对象(Invoker)与被调用对象(Image、Text)之间的耦合关系解除。真正调用对象操作的是具体实现的命令对象,它把具体操作封装在execute内部,并为之实现了逆向的操作(如果可以的话)。而原先的调用者(invoker)只需要执行命令对象(Command)的execute和unExecute操作即可,而并不知道它操作了什么样的具体对象。使用Command将对象间的调用耦合关系解除,同时也获得了用户操作的历史信息,把这些信息记录在一个队列内部,通过顺序访问队列重复执行unExecute和excute操作即可完成撤销和恢复的功能。原则上撤销和执行的操作是相互抵消的,但是有时这并不能精确保证,为了解决类似问题可以使用备忘录模式精确记录对象状态。另外,在命令对象继承层次中引入组合模式可以实现宏命令的功能。
举例:
为了方便理解,我们把调用者(invoker)看作GUI的控件对象,用户操作空间要改变图片的大小的时候会通过多态性质调用ResizeCommand的execute操作,而该execute会执行Image的resize操作。而用户要想撤销该操作,只需要访问命令队列,执行该对象的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
- 剑指offer代码解析——面试题21包含min函数的栈
- 剑指offer代码解析——面试题19二叉树的镜像
- mysql高可用架构设计,处理高并发,大流量!
- 零基础入门深度学习 | 第三章:神经网络和反向传播算法
- 微信企业付款到个人钱包引发的坑之反思~!
- Intellij idea创建javaWeb以及Servlet简单实现
- 设计模式之代理模式之读写分离!!!
- Phantomjs+Nodejs+Mysql数据抓取(1.数据抓取)
- Phantomjs+Nodejs+Mysql数据抓取(2.抓取图片)
- 深入浅出Redis-redis底层数据结构(上)
- Linux下自动化监控内存、存储空间!
- 深入浅出Redis-redis底层数据结构(下)
- Spring-boot:快速搭建微框架服务
- Mysql重要参数说明
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Linux下卸载MySQL8.0版本的操作方法
- Linux服务器上安装Python3的两种方式
- Centos7安装ElasticSearch 6.4.1入门教程详解
- Windows 和 Linux 上Redis的安装守护进程配置方法
- 在Linux系统上安装Spring boot应用的教程详解
- 使用openssl 生成免费证书的方法步骤
- linux cd的含义以及用法
- leetcode栈之比较含退格的字符串
- CentOS使用本地yum源搭建LAMP环境图文教程
- 清除CentOS 6或CentOS 7上的磁盘空间的方法
- leetcode栈之二叉树的前序遍历
- 解决Linux下Mysql5.7忘记密码问题
- CentOS8.0 安装配置ftp服务器的实现方法
- Linux实现自动登录的实例讲解
- Linux中date命令转换日期提示date: illegal time format问题解决