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

Liuwei-Sunny發表於2012-05-02

21.2 備忘錄模式概述

      備忘錄模式提供了一種狀態恢復的實現機制,使得使用者可以方便地回到一個特定的歷史步驟,當新的狀態無效或者存在問題時,可以使用暫時儲存起來的備忘錄將狀態復原,當前很多軟體都提供了撤銷(Undo)操作,其中就使用了備忘錄模式。

      備忘錄模式定義如下:

備忘錄模式(Memento Pattern):在不破壞封裝的前提下,捕獲一個物件的內部狀態,並在該物件之外儲存這個狀態,這樣可以在以後將物件恢復到原先儲存的狀態。它是一種物件行為型模式,其別名為Token

      備忘錄模式的核心是備忘錄類以及用於管理備忘錄的負責人類的設計,其結構如圖21-3所示:

      在備忘錄模式結構圖中包含如下幾個角色:

      Originator(原發器):它是一個普通類,可以建立一個備忘錄,並儲存它的當前內部狀態,也可以使用備忘錄來恢復其內部狀態,一般將需要儲存內部狀態的類設計為原發器。

     Memento(備忘錄)儲存原發器的內部狀態,根據原發器來決定儲存哪些內部狀態。備忘錄的設計一般可以參考原發器的設計,根據實際需要確定備忘錄類中的屬性。需要注意的是,除了原發器本身與負責人類之外,備忘錄物件不能直接供其他類使用,原發器的設計在不同的程式語言中實現機制會有所不同。

      Caretaker(負責人):負責人又稱為管理者,它負責儲存備忘錄,但是不能對備忘錄的內容進行操作或檢查。在負責人類中可以儲存一個或多個備忘錄物件,它只負責儲存物件,而不能修改物件,也無須知道物件的實現細節。

      理解備忘錄模式並不難,但關鍵在於如何設計備忘錄類和負責人類。由於在備忘錄中儲存的是原發器的中間狀態,因此需要防止原發器以外的其他物件訪問備忘錄,特別是不允許其他物件來修改備忘錄。

      下面我們通過簡單的示例程式碼來說明如何使用Java語言實現備忘錄模式:

      在使用備忘錄模式時,首先應該存在一個原發器類Originator,在真實業務中,原發器類是一個具體的業務類,它包含一些用於儲存成員資料的屬性,典型程式碼如下所示:

package dp.memento;
public class Originator {
    private String state;

    public Originator(){}

  // 建立一個備忘錄物件
    public Memento createMemento() {
    return new Memento(this);
    }

  // 根據備忘錄物件恢復原發器狀態
    public void restoreMemento(Memento m) {
     state = m.state;
    }

    public void setState(String state) {
        this.state=state;
    }

    public String getState() {
        return this.state;
    }
}

      對於備忘錄類Memento而言,它通常提供了與原發器相對應的屬性(可以是全部,也可以是部分)用於儲存原發器的狀態,典型的備忘錄類設計程式碼如下:

package dp.memento;
//備忘錄類,預設可見性,包內可見
class Memento {
    private String state;

    public Memento(Originator o) {
    state = o.getState();
    }

    public void setState(String state) {
        this.state=state;
    }

    public String getState() {
        return this.state;
    }
}

      在設計備忘錄類時需要考慮其封裝性除了Originator類,不允許其他類來呼叫備忘錄類Memento的建構函式與相關方法如果不考慮封裝性,允許其他類呼叫setState()等方法,將導致在備忘錄中儲存的歷史狀態發生改變,通過撤銷操作所恢復的狀態就不再是真實的歷史狀態,備忘錄模式也就失去了本身的意義。

      在使用Java語言實現備忘錄模式時,一般通過將Memento類與Originator類定義在同一個包(package)中來實現封裝,在Java語言中可使用預設訪問識別符號來定義Memento類,即保證其包內可見。只有Originator類可以對Memento進行訪問,而限制了其他類對Memento的訪問。在 Memento中儲存了Originatorstate值,如果Originator中的state值改變之後需撤銷,可以通過呼叫它的restoreMemento()方法進行恢復。

      對於負責人類Caretaker,它用於儲存備忘錄物件,並提供getMemento()方法用於向客戶端返回一個備忘錄物件,原發器通過使用這個備忘錄物件可以回到某個歷史狀態。典型的負責人類的實現程式碼如下:

package dp.memento;
public class Caretaker {
	private Memento memento;

	public Memento getMemento() {
		return memento;
	}

	public void setMemento(Memento memento) {
		this.memento=memento;
	}
}

      Caretaker類中不應該直接呼叫Memento中的狀態改變方法,它的作用僅僅用於儲存備忘錄物件。將原發器備份生成的備忘錄物件儲存在其中,當使用者需要對原發器進行恢復時再將儲存在其中的備忘錄物件取出。

 

思考

能否通過原型模式來建立備忘錄物件?系統該如何設計?

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

相關文章