命令模式的主要作用是将调用者与接收者进行解耦;将一个方法的调用封装成一个命令对象,因此调用者就无需了解内部的执行过程;而且当需要执行新的请求时,无需改变现有代码,只需要给调用者传递一个命令对象就可;
组成及关系图如下:
简单demo
- client 客户端:关联invoker编译期就决定,依赖commond运行期才能决定
public class Client {
public static void main(String[] args) {
Receiver receiver = new Receiver();
Commond commond = new ConcreteCommond(receiver);
Invoker invoker = new Invoker();
invoker.setCommond(commond);
invoker.action();
}
}
复制代码
- invoker
调用者与common为组合关系
public class Invoker {
private Commond commond;
public void setCommond(Commond commond) {
this.commond = commond;
}
public void action() {
commond.excute();
}
}
复制代码
- command 命令的抽象,是一个接口,约定了excute方法
public interface Commond {
void excute();
}
复制代码
- concreteCommond 具体的命令 与接收者为关联关系,编译期就决定
public class ConcreteCommond implements Commond{
private Receiver receiver;
public ConcreteCommond(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void excute() {
receiver.doSomething();
}
}
复制代码
- receiver 接收者,也是命令的具体执行者。
public class Receiver {
public void doSomething() {
System.out.println("receiver do something....");
}
}
复制代码
由以上简单代码实例就可以看出,调用者与执行者没有直接关联,需要执行其他命令时,只需要新建一个命令将其赋予invoker就可以了,但是这样做也会产生一些缺点,系统中可能会出现许多命令类
单步undo操作
现在先写一个简单的能进行单步撤销的demo,模仿的记事本
receiver
public class Note {
//输出文本内容
public void write (String content) {
System.out.println(content);
}
}
复制代码
command
public interface Commond {
//写入字符
void excute(String content);
//撤销
void undo();
}
复制代码
concreteCommand
public class WriteCommond implements Commond {
private Note note;
private String commondContent = ""; //当前文本的内容
private int length = 0; //保存一次操作写入的字符长度
public WriteCommond(Note note) {
this.note = note;
}
@Override
public void excute(String content) {
commondContent += content;
note.write("现在 "+commondContent);
length = content.length();
}
@Override
public void undo() {
//把最近一次输入长度的字符穿丢弃
commondContent = commondContent.substring(0,commondContent.length()-length);
note.write("撤销: "+commondContent);
}
}
复制代码
invoker
public class Invoker {
private Commond commond;
public void setCommond(Commond commond) {
this.commond = commond;
}
public void write(String s) {
commond.excute(s);
}
public void undo() {
commond.undo();
}
}
复制代码
client
public class Client {
public static void main(String[] args) {
Note note = new Note();
Commond commond = new WriteCommond(note);
Invoker invoker = new Invoker();
invoker.setCommond(commond);
invoker.write("aaaa"); //第一次写入aaaaa
invoker.write("sss"); //再次写入sss,文本内容就变为aaaasss
invoker.undo(); //撤销就变回aaaaa
}
}
复制代码
执行结果如下:
多步undo、redo
receiver
public class Note {
public void write (String content) {
System.out.println(content);
}
}
复制代码
command
public interface Commond {
//执行
void excute(String content);
//撤销
void undo();
//取消撤销
void redo();
}
复制代码
concreteCommand
public class WriteCommond implements Commond {
private Note note;
private String commondContent = ""; //当前文本的内容
private Stack<Integer> lengths = new Stack<>();//保存每次操作写入的字符长度,用于撤销
private Stack<String> Strings = new Stack<>(); //每次撤销掉的字符串,用于取消撤销
public WriteCommond(Note note) {
this.note = note;
}
@Override
public void excute(String content) {
commondContent += content;
note.write("现在 "+commondContent);
lengths.push(content.length());
if (!Strings.empty()) {
Strings.removeAllElements(); //执行了写操作,跟新至最新,清空取消撤销的内容
}
}
@Override
public void undo() {
if (lengths.empty()) {
System.out.println("无法继续ctrl+z");
return;
}
Integer length = lengths.pop(); //撤销长度
int contentcLength = commondContent.length(); //内容长度
String redoString = commondContent.substring(contentcLength-length,contentcLength);//撤销掉的字符串
Strings.push(redoString); //撤销的内容 放入redo容器
//把最近一次输入长度的字符穿丢弃,及执行撤销
String outContent = commondContent.substring(0, contentcLength -length);
commondContent = outContent;
note.write("撤销: "+outContent);
}
@Override
public void redo() {
if (Strings.empty()) {
System.out.println("已是最新状态,无法继续ctrl+y");
return ;
}
String redoString = Strings.pop();
commondContent += redoString;
note.write("取消撤销: "+commondContent);
lengths.push(redoString.length());
}
}
复制代码
invoker
public class Invoker {
private Commond commond;
public void setCommond(Commond commond) {
this.commond = commond;
}
public void write(String s) {
commond.excute(s);
}
public void undo() {
commond.undo();
}
public void redo() {
commond.redo();
}
}
复制代码
client
public class Client {
public static void main(String[] args) {
Note note = new Note();
Commond commond = new WriteCommond(note);
Invoker invoker = new Invoker();
invoker.setCommond(commond);
invoker.undo();
invoker.write("aaaa"); //第一次写入aaaaa
invoker.write("sss"); //再次写入sss,文本内容就变为aaaasss
invoker.undo(); //撤销就变回aaaaa
invoker.undo(); //再撤销就变回""
invoker.redo(); //取消撤销就变回aaaaa
invoker.undo(); //撤销又变回" "
}
}
复制代码
执行结果
其实主要就是在concreteCommond中记录一些状态、标志用于执行相关的redo、undo