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

Liuwei-Sunny發表於2012-05-02

21.4 實現多次撤銷

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

      改進之後的中國象棋棋子撤銷功能結構圖如圖21-5所示:

      在圖21-5中,我們對負責人類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; //定義一個索引來記錄當前狀態所在位置
	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()); //儲存備忘錄
		index ++; 
		System.out.println("棋子" + chess.getLabel() + "當前位置為:" + "第" + 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() + "當前位置為:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");
	}

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

      編譯並執行程式,輸出結果如下:

棋子車當前位置為:第1行第1列。

棋子車當前位置為:第1行第4列。

棋子車當前位置為:第5行第4列。

******悔棋******

棋子車當前位置為:第1行第4列。

******悔棋******

棋子車當前位置為:第1行第1列。

******撤銷悔棋******

棋子車當前位置為:第1行第4列。

******撤銷悔棋******

棋子車當前位置為:第5行第4列。

 

 

擴充套件

本例項只能實現最簡單的UndoRedo操作,並未考慮物件狀態在操作過程中出現分支的情況。如果在撤銷到某個歷史狀態之後,使用者再修改物件狀態,此後執行Undo操作時可能會發生物件狀態錯誤,大家可以思考其產生原因。【注:可將物件狀態的改變繪製成一張樹狀圖進行分析。】

在實際開發中,可以使用連結串列或者堆疊來處理有分支的物件狀態改變,大家可通過連結串列或者堆疊對上述例項進行改進。

【作者:劉偉  http://blog.csdn.net/lovelion

相關文章