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)改變,大家可通過鏈表或者堆棧對上述實例進行改進。