命令模式
定义与类型
- 定义: 命令模式将一个请求封装成对象,让用户使用不同的请求将客户端参数化。
- 类型: 行为型
适用场景
- 想用对象参数化一个动作以执行操作,并且用不同命令对象来替换回调函数
- 应用程序支持撤销和恢复
- 记录请求日志,当系统故障这些命令可以重新被执行
优点
- 能较容易的设计一个命令队列
- 在需要的情况下,可以容易的将命令计入日志
- 允许接收请求的一方决定是否否决请求
- 可以容易的实现对请求的撤销和重做
- 由于加进新的具体命令类不影响其他的类,因此便于扩展
- 命令模式将请求一个操作的对象与具体执行一个操作的对象分割开,符合开闭原则和迪米特法则
缺点
角色划分
- 角色一:接收者(Receiver作用:负责具体的功能实现->具体实现细节)(非必须,也可以是个方法)
- 角色二:命令接口(协议)(Command:命令抽象)
- 角色三:具体的命令(ConcreteCmmand:具体的命令,作用:负责调用接收者逻辑方法,行为方法)
- 角色四:请求者角色(Invoker:调用执行命令)
- 角色五:客户端
结构图
引用一下大话设计模式中的结构图
提出需求
实现undo和redo的功能
分析需求
undo好redo的功能的本质是命令队列中的命令的规律执行。闲言碎语不要讲,咱们直接开撸。
代码结构

SQLExcute扮演的是Receiver,CommandManager扮演Invoke,其他的两个角色就一目了然了。我们看下每个类具体的代码。
- ICommand
/**
* @Author: ming.wang
* @Date: 2019/3/7 16:42
* @Description:
*/
public interface ICommand {
void uodo();
void excute();
}
- InsertIntoCommand
/**
* @Author: ming.wang
* @Date: 2019/3/11 17:23
* @Description:
*/
@Getter
@Setter
@ToString
public class InsertIntoCommand implements ICommand {
private SQLExcute sqlExcute;
private String id;
public InsertIntoCommand(SQLExcute sqlExcute, String id) {
this.sqlExcute = sqlExcute;
this.id = id;
}
@Override
public void uodo() {
sqlExcute.delete(id);
}
@Override
public void excute() {
sqlExcute.insertInto(id);
}
}
- SQLExcute
/**
* @Author: ming.wang
* @Date: 2019/3/12 9:52
* @Description:
*/
@Getter
@Setter
@ToString
public class SQLExcute {
public void insertInto(String id) {
System.out.println("插入一条数据,id:" + id);
}
public void delete(String id) {
System.out.println("删除一条数据,id:" + id);
}
}
- CommandManager
/**
* @Author: ming.wang
* @Date: 2019/3/11 17:26
* @Description:
*/
public class CommandManager {
private Stack<ICommand> redoStacks = new Stack<>();
private Stack<ICommand> undoStacks = new Stack<>();
public void excute(ICommand iCommand) {
iCommand.excute();
undoStacks.push(iCommand);
if (!redoStacks.isEmpty()) {
redoStacks.clear();
}
}
public void uodo() {
if (!undoStacks.isEmpty()) {
ICommand pop = undoStacks.pop();
pop.uodo();
redoStacks.push(pop);
}
}
public void redo() {
if (!redoStacks.isEmpty()) {
ICommand pop = redoStacks.pop();
pop.excute();
}
}
}
- Test
/**
* @Author: ming.wang
* @Date: 2019/3/11 16:44
* @Description:
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
CommandManager commandManager = new CommandManager();
SQLExcute sqlExcute = new SQLExcute();
InsertIntoCommand intoCommand1 = new InsertIntoCommand(sqlExcute, "1");
InsertIntoCommand intoCommand2 = new InsertIntoCommand(sqlExcute, "2");
System.out.println("执行命令");
commandManager.excute(intoCommand1);
commandManager.excute(intoCommand2);
System.out.println("执行undo");
commandManager.uodo();
commandManager.uodo();
System.out.println("执行redo");
commandManager.redo();
commandManager.redo();
}
}
运行结果
