鍍金池/ 教程/ Java/ 撤銷功能的實現(xiàn)——備忘錄模式(四)
請求發(fā)送者與接收者解耦——命令模式(四)
對象間的聯(lián)動——觀察者模式(三)
算法的封裝與切換——策略模式(三)
請求發(fā)送者與接收者解耦——命令模式(三)
遍歷聚合對象中的元素——迭代器模式(四)
請求的鏈?zhǔn)教幚怼氊?zé)鏈模式(二)
自定義語言的實現(xiàn)——解釋器模式(五)
操作復(fù)雜對象結(jié)構(gòu)——訪問者模式(一)
處理對象的多種狀態(tài)及其相互轉(zhuǎn)換——狀態(tài)模式(一)
處理對象的多種狀態(tài)及其相互轉(zhuǎn)換——狀態(tài)模式(三)
操作復(fù)雜對象結(jié)構(gòu)——訪問者模式(二)
協(xié)調(diào)多個對象之間的交互——中介者模式(二)
算法的封裝與切換——策略模式(四)
請求發(fā)送者與接收者解耦——命令模式(六)
撤銷功能的實現(xiàn)——備忘錄模式(二)
算法的封裝與切換——策略模式(一)
遍歷聚合對象中的元素——迭代器模式(五)
模板方法模式深度解析(三)
協(xié)調(diào)多個對象之間的交互——中介者模式(五)
自定義語言的實現(xiàn)——解釋器模式(三)
處理對象的多種狀態(tài)及其相互轉(zhuǎn)換——狀態(tài)模式(六)
遍歷聚合對象中的元素——迭代器模式(三)
操作復(fù)雜對象結(jié)構(gòu)——訪問者模式(四)
遍歷聚合對象中的元素——迭代器模式(一)
算法的封裝與切換——策略模式(二)
請求的鏈?zhǔn)教幚怼氊?zé)鏈模式(一)
遍歷聚合對象中的元素——迭代器模式(二)
操作復(fù)雜對象結(jié)構(gòu)——訪問者模式(三)
對象間的聯(lián)動——觀察者模式(六)
對象間的聯(lián)動——觀察者模式(五)
請求發(fā)送者與接收者解耦——命令模式(一)
自定義語言的實現(xiàn)——解釋器模式(六)
自定義語言的實現(xiàn)——解釋器模式(一)
模板方法模式深度解析(一)
撤銷功能的實現(xiàn)——備忘錄模式(一)
處理對象的多種狀態(tài)及其相互轉(zhuǎn)換——狀態(tài)模式(五)
請求的鏈?zhǔn)教幚怼氊?zé)鏈模式(三)
遍歷聚合對象中的元素——迭代器模式(六)
撤銷功能的實現(xiàn)——備忘錄模式(三)
處理對象的多種狀態(tài)及其相互轉(zhuǎn)換——狀態(tài)模式(四)
處理對象的多種狀態(tài)及其相互轉(zhuǎn)換——狀態(tài)模式(二)
協(xié)調(diào)多個對象之間的交互——中介者模式(四)
對象間的聯(lián)動——觀察者模式(二)
請求發(fā)送者與接收者解耦——命令模式(二)
自定義語言的實現(xiàn)——解釋器模式(四)
對象間的聯(lián)動——觀察者模式(四)
撤銷功能的實現(xiàn)——備忘錄模式(五)
自定義語言的實現(xiàn)——解釋器模式(二)
協(xié)調(diào)多個對象之間的交互——中介者模式(三)
協(xié)調(diào)多個對象之間的交互——中介者模式(一)
撤銷功能的實現(xiàn)——備忘錄模式(四)
模板方法模式深度解析(二)
撤銷功能的實現(xiàn)——備忘錄模式(五)
請求發(fā)送者與接收者解耦——命令模式(五)
請求的鏈?zhǔn)教幚怼氊?zé)鏈模式(四)

撤銷功能的實現(xiàn)——備忘錄模式(四)

實現(xiàn)多次撤銷

Sunny 軟件公司開發(fā)人員通過使用備忘錄模式實現(xiàn)了中國象棋棋子的撤銷操作,但是使用上述代碼只能實現(xiàn)一次撤銷,因為在負責(zé)人類中只定義一個備忘錄對象來保存狀態(tài),后面保存的狀態(tài)會將前一次保存的狀態(tài)覆蓋,但有時候用戶需要撤銷多步操作。如何實現(xiàn)多次撤銷呢?本節(jié)將提供一種多次撤銷的解決方案,那就是在負責(zé)人類中定義一個集合來存儲多個備忘錄,每個備忘錄負責(zé)保存一個歷史狀態(tài),在撤銷時可以對備忘錄集合進行逆向遍歷,回到一個指定的歷史狀態(tài),而且還可以對備忘錄集合進行正向遍歷,實現(xiàn)重做(Redo)操作,即取消撤銷,讓對象狀態(tài)得到恢復(fù)。

改進之后的中國象棋棋子撤銷功能結(jié)構(gòu)圖如圖所示:

http://wiki.jikexueyuan.com/project/design-pattern-behavior/images/1335892489_9232.jpg" alt="" />

在圖中,我們對負責(zé)人類 MementoCaretaker 進行了修改,在其中定義了一個 ArrayList 類型的集合對象來存儲多個備忘錄,其代碼如下所示:

import java.util.*;

class MementoCaretaker {
    //定義一個集合來存儲多個備忘錄
    private ArrayList mementolist = new ArrayList();

    public ChessmanMemento getMemento(int i) {
        return (ChessmanMemento)mementolist.get(i);
    }

    public void setMemento(ChessmanMemento memento) {
        mementolist.add(memento);
    }
}

編寫如下客戶端測試代碼:

class Client {
private static int index = -1; //定義一個索引來記錄當(dāng)前狀態(tài)所在位置
    private static MementoCaretaker mc = new MementoCaretaker();

    public static void main(String args[]) {
        Chessman chess = new Chessman("車",1,1);
        play(chess);        
        chess.setY(4);
        play(chess);
        chess.setX(5);
        play(chess);    
        undo(chess,index);
        undo(chess,index);  
        redo(chess,index);
        redo(chess,index);
    }

    //下棋
    public static void play(Chessman chess) {
        mc.setMemento(chess.save()); //保存?zhèn)渫?        index ++; 
        System.out.println("棋子" + chess.getLabel() + "當(dāng)前位置為:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");
    }

    //悔棋
    public static void undo(Chessman chess,int i) {
        System.out.println("******悔棋******");
        index --; 
        chess.restore(mc.getMemento(i-1)); //撤銷到上一個備忘錄
        System.out.println("棋子" + chess.getLabel() + "當(dāng)前位置為:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");
    }

    //撤銷悔棋
    public static void redo(Chessman chess,int i) {
        System.out.println("******撤銷悔棋******"); 
        index ++; 
        chess.restore(mc.getMemento(i+1)); //恢復(fù)到下一個備忘錄
        System.out.println("棋子" + chess.getLabel() + "當(dāng)前位置為:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");
    }
} 

編譯并運行程序,輸出結(jié)果如下:

棋子車當(dāng)前位置為:第1行第1列。
棋子車當(dāng)前位置為:第1行第4列。
棋子車當(dāng)前位置為:第5行第4列。
******悔棋******
棋子車當(dāng)前位置為:第1行第4列。
******悔棋******
棋子車當(dāng)前位置為:第1行第1列。
******撤銷悔棋******
棋子車當(dāng)前位置為:第1行第4列。
******撤銷悔棋******
棋子車當(dāng)前位置為:第5行第4列。

擴展

本實例只能實現(xiàn)最簡單的 Undo 和 Redo 操作,并未考慮對象狀態(tài)在操作過程中出現(xiàn)分支的情況。如果在撤銷到某個歷史狀態(tài)之后,用戶再修改對象狀態(tài),此后執(zhí)行 Undo 操作時可能會發(fā)生對象狀態(tài)錯誤,大家可以思考其產(chǎn)生原因?!咀ⅲ嚎蓪ο鬆顟B(tài)的改變繪制成一張樹狀圖進行分析?!?

在實際開發(fā)中,可以使用鏈表或者堆棧來處理有分支的對象狀態(tài)改變,大家可通過鏈表或者堆棧對上述實例進行改進。