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中儲存了Originator的state值,如果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中的狀態改變方法,它的作用僅僅用於儲存備忘錄對象。將原發器備份產生的備忘錄Object Storage Service在其中,當使用者需要對原發器進行恢複時再將儲存在其中的備忘錄對象取出。
|
思考 能否通過原型模式來建立備忘錄對象?系統該如何設計? |
|
【作者:劉偉 http://blog.csdn.net/lovelion】