前言:命令模式内容比较多,这里做了拆分
命令模式基础篇 :http://www.cnblogs.com/JsonShare/p/7202133.html
命令模式扩展篇 - 宏命令:http://www.cnblogs.com/JsonShare/p/7206395.html
命令模式扩展篇 - 撤销命令:http://www.cnblogs.com/JsonShare/p/7206513.html
命令模式扩展篇 - 命令队列:http://www.cnblogs.com/JsonShare/p/7206607.html
命令模式扩展篇 - 请求日志:http://www.cnblogs.com/JsonShare/p/7206665.html
2-1、撤销命令
有两种基本的思路来实现可撤销的操作:
1、逆向操作实现:比如被撤销的操作是添加功能,那撤消的实现就变成删除功能;同理被撤销的操作是删除功能,那么撤销的实现就变成添加功能;
2、存储恢复实现,意思就是把操作前的状态记录下来,然后要撤销操作的时候就直接恢复回去就可以了,可使用备忘录模式(Memento Pattern)来实现(稍后会讲到备忘录模式);
这里主要讲解通过逆向操作来实现撤销(undo):
具体操作:在命令模式中,我们可以通过调用一个命令对象的execute()方法来实现对请求的处理,如果需要撤销(undo)请求,可通过在命令类中增加一个逆向操作来实现。
修改Command接口:
/** * 包含撤销命令的接口 */ public interface Command { //执行方法 void execute(); //撤销方法 void undo(); }
下面实例说明:
package com.designpattern.Command.extend.Undo; /** * 包含撤销命令的接口 * @author Json */ public interface Command { //执行方法 void execute(); //撤销方法 void undo(); }
package com.designpattern.Command.extend.Undo; /** * 具体命令 -- 创建目录 * @author Json */ public class CreateDirCommand implements Command { private String dirName; MakeDir makeDir; public CreateDirCommand(MakeDir makeDir,String dirName) { this.makeDir = makeDir; this.dirName = dirName; } @Override public void execute() { makeDir.createDir(dirName); } @Override public void undo() { makeDir.deleteDir(dirName); } }
package com.designpattern.Command.extend.Undo; import java.io.File; /** * 命令接受者 * 包含两个命令:创建目录、删除目录 * @author Json */ public class MakeDir { //创建目录 public void createDir(String name){ File dir = new File(name); if (dir.exists()) { System.out.println("创建目录 " + name + " 失败,目标目录已经存在"); }else{ //创建目录 if (dir.mkdirs()) { System.out.println("创建目录 " + name + " 成功"); } else { System.out.println("创建目录 " + name + " 失败"); } } } //删除目录 public void deleteDir(String name){ File dir = new File(name); if(dir.exists()) { if(dir.delete()){ System.out.println("删除目录 " + name + " 成功"); }else{ System.out.println("删除目录 " + name + " 失败"); } }else{ System.out.println("删除目录 " + name + " 失败,目标目录不存在"); } } }
package com.designpattern.Command.extend.Undo; /** * 请求者 * @author Json */ public class RequestMakeDir { Command createCommand; public void setCreateCommand(Command command) { this.createCommand = command; } public void executeCreateCommand(){ createCommand.execute(); } public void undoCreateCommand(){ createCommand.undo(); } }
测试:
package com.designpattern.Command.extend.Undo; /** * 测试 * @author Json */ public class Client { public static void main(String[] args) { String dir = "E:\command2017"; //创建接收者 MakeDir makeDir = new MakeDir(); //创建具体命令并且指定接收者 Command create_command = new CreateDirCommand(makeDir,dir); //创建请求者 RequestMakeDir request = new RequestMakeDir(); //设置命令 request.setCreateCommand(create_command); /***********创建目录及撤销**************/ //创建目录 request.executeCreateCommand(); //撤销 request.undoCreateCommand(); } }
结果:
创建目录 E:command2017 成功
删除目录 E:command2017 成功
上面例子很简单,大家应该明白了吧!!!
深入一下,提个问题:如何支持多次撤销? 下面有讲解,继续往下看↓↓↓
2-2、多次撤销
我们在使用文本编辑器的时候,经常会遇到撤销的操作,这是就按一下组合键(Ctrl + Z),就会回到上次修改,至于代码要如何实现?
首先还是要先实现各个命令的undo行为(执行与execute相反顺序的操作即可),另外我们还需要一个栈来记录已经被执行过的操作,以支持撤销到初始状态;
下面来改造一下实例:
package com.designpattern.Command.extend.MultiUndo; /** * 包含撤销命令的接口 * @author Json */ public interface Command { //执行方法 void execute(String dirName); //撤销方法 void undo(); }
package com.designpattern.Command.extend.MultiUndo; import java.util.ArrayList; /** * 具体命令 -- 创建目录 * @author Json */ public class CreateDirCommand implements Command { private ArrayList<String> dirNameList; //记录 这里用list实现 private MakeDir makeDir; public CreateDirCommand(MakeDir makeDir) { dirNameList = new ArrayList<String>(); this.makeDir = makeDir; } @Override public void execute(String dirName) { makeDir.createDir(dirName); dirNameList.add(dirName); } @Override public void undo() { if(dirNameList.size()>0){ makeDir.deleteDir(dirNameList.get(dirNameList.size()-1)); dirNameList.remove(dirNameList.size()-1); }else{ System.out.println("没有需要撤销的操作!"); } } }
package com.designpattern.Command.extend.MultiUndo; import java.io.File; /** * 命令接受者 * 包含两个命令:创建目录、删除目录 * @author Json */ public class MakeDir { //创建目录 public void createDir(String name){ File dir = new File(name); if (dir.exists()) { System.out.println("创建目录 " + name + " 失败,目标目录已经存在"); }else{ //创建目录 if (dir.mkdirs()) { System.out.println("创建目录 " + name + " 成功"); } else { System.out.println("创建目录 " + name + " 失败"); } } } //删除目录 public void deleteDir(String name){ File dir = new File(name); if(dir.exists()) { if(dir.delete()){ System.out.println("删除目录 " + name + " 成功"); }else{ System.out.println("删除目录 " + name + " 失败"); } }else{ System.out.println("删除目录 " + name + " 失败,目标目录不存在"); } } }
package com.designpattern.Command.extend.MultiUndo; /** * 请求者 * @author Json */ public class RequestMakeDir { Command createCommand; public void setCreateCommand(Command command) { this.createCommand = command; } public void executeCreateCommand(String dirName){ createCommand.execute(dirName); } public void undoCreateCommand(){ createCommand.undo(); } }
测试:
package com.designpattern.Command.extend.MultiUndo; /** * 测试 * @author Json */ public class Client { public static void main(String[] args) { //创建接收者 MakeDir makeDir = new MakeDir(); //创建具体命令并且指定接收者 Command create_command = new CreateDirCommand(makeDir); //创建请求者 RequestMakeDir request = new RequestMakeDir(); //设置命令 request.setCreateCommand(create_command); /***********创建目录及撤销**************/ //创建目录 request.executeCreateCommand("E:\command\2017"); request.executeCreateCommand("E:\command\2018"); request.executeCreateCommand("E:\command\2019"); request.executeCreateCommand("E:\command\2020"); //多次撤销 request.undoCreateCommand(); request.undoCreateCommand(); request.undoCreateCommand(); request.undoCreateCommand(); request.undoCreateCommand(); } }
结果:
创建目录 E:command2017 成功
创建目录 E:command2018 成功
创建目录 E:command2019 成功
创建目录 E:command2020 成功
删除目录 E:command2020 成功
删除目录 E:command2019 成功
删除目录 E:command2018 成功
删除目录 E:command2017 成功
没有需要撤销的操作!
上面的例子存在瑕疵:不知道大家有没有看出来:
我们没有保存命令对象的历史状态(执行情况,比如成功、失败),可以通过引入一个命令集合或其他方式来存储每一次操作时命令的状态,这样实现多次撤销操作就完美了(这里就不在列举实例了,请自行撸代码吧)。
PS:除了undo操作外,还可以采用同undo类似的方式实现恢复(redo)操作,即恢复所撤销的操作(或称为二次撤销)。
PS:源码地址 https://github.com/JsonShare/DesignPattern/tree/master
PS:源码地址 http://www.cnblogs.com/JsonShare/p/7206513.html