設計模式解密(11)- 命令模式 - 擴展篇(撤銷命令)


前言:命令模式內容比較多,這里做了拆分

命令模式基礎篇 :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:\command\2017 成功
創建目錄 E:\command\2018 成功
創建目錄 E:\command\2019 成功
創建目錄 E:\command\2020 成功
刪除目錄 E:\command\2020 成功
刪除目錄 E:\command\2019 成功
刪除目錄 E:\command\2018 成功
刪除目錄 E:\command\2017 成功
沒有需要撤銷的操作!

上面的例子存在瑕疵:不知道大家有沒有看出來:

我們沒有保存命令對象的歷史狀態(執行情況,比如成功、失敗),可以通過引入一個命令集合或其他方式來存儲每一次操作時命令的狀態,這樣實現多次撤銷操作就完美了(這里就不在列舉實例了,請自行擼代碼吧)。


PS:除了undo操作外,還可以采用同undo類似的方式實現恢復(redo)操作,即恢復所撤銷的操作(或稱為二次撤銷)。

 

PS:源碼地址   https://github.com/JsonShare/DesignPattern/tree/master 

  

PS:源碼地址 http://www.cnblogs.com/JsonShare/p/7206513.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM