文章目錄
在閻宏博士的《JAVA與模式》一書中開頭是這樣描述備忘錄(Memento)模式的:
備忘錄模式又叫做快照模式(Snapshot Pattern)或Token模式,是對象的行為模式。
備忘錄對象是一個用來儲存另外一個對象內部狀態的快照的對象。備忘錄模式的用意是在不破壞封裝的條件下,將一個對象的狀態捕捉(Capture)住,並外部化,儲存起來,從而可以在將來合適的時候把這個對象還原到儲存起來的狀態。備忘錄模式常常與命令模式和迭代子模式一同使用。
備忘錄模式的結構
備忘錄模式的結構圖如下所示
備忘錄模式所涉及的角色有三個:備忘錄(Memento)角色、發起人(Originator)角色、負責人(Caretaker)角色。
備忘錄(Memento)角色
備忘錄角色又如下責任:
(1)將發起人(Originator)對象的內戰狀態儲存起來。備忘錄可以根據發起人對象的判斷來決定儲存多少發起人(Originator)對象的內部狀態。
(2)備忘錄可以保護其內容不被發起人(Originator)對象之外的任何對象所讀取。
備忘錄有兩個等效的介面:
● 窄介面:負責人(Caretaker)對象(和其他除發起人對象之外的任何對象)看到的是備忘錄的窄介面(narrow interface),這個窄介面只允許它把備忘錄對象傳給其他的對象。
● 寬介面:與負責人對象看到的窄介面相反的是,發起人對象可以看到一個寬介面(wide interface),這個寬介面允許它讀取所有的資料,以便根據這些資料恢複這個發起人對象的內部狀態。
發起人(
Originator)角色
發起人角色有如下責任:
(1)建立一個含有當前的內部狀態的備忘錄對象。
(2)使用備忘錄Object Storage Service其內部狀態。
負責人(
Caretaker)角色
負責人角色有如下責任:
(1)負責儲存備忘錄對象。
(2)不檢查備忘錄對象的內容。
“白箱”備忘錄模式的實現
備忘錄角色對任何對象都提供一個介面,即寬介面,備忘錄角色的內部所儲存的狀態就對所有對象公開。因此這個實現又叫做“白箱實現”。
“白箱”實現將發起人角色的狀態儲存在一個大家都看得到的地方,因此是破壞封裝性的。但是通過程式員自律,同樣可以在一定程度上實現模式的大部分用意。因此白箱實現仍然是有意義的。
下面給出一個示意性的“白箱實現”。
原始碼
發起人角色類,發起人角色利用一個新建立的備忘錄對象將自己的內部狀態儲存起來。
public class Originator { private String state; /** * Factory 方法,返回一個新的備忘錄對象 */ public Memento createMemento(){ return new Memento(state); } /** * 將發起人恢複到備忘錄對象所記載的狀態 */ public void restoreMemento(Memento memento){ this.state = memento.getState(); } public String getState() { return state; } public void setState(String state) { this.state = state; System.out.println("目前狀態:" + this.state); } }
備忘錄角色類,備忘錄對象將發起人對象傳入的狀態儲存起來。
public class Memento { private String state; public Memento(String state){ this.state = state; } public String getState() { return state; } public void setState(String state) { this.state = state; } }
負責人角色類,負責人角色負責儲存備忘錄對象,但是從不修改(甚至不查看)備忘錄對象的內容。
public class Caretaker { private Memento memento; /** * 備忘錄的取值方法 */ public Memento retrieveMemento(){ return this.memento; } /** * 備忘錄的賦值方法 */ public void saveMemento(Memento memento){ this.memento = memento; }}
用戶端角色類
public class Client { public static void main(String[] args) { Originator o = new Originator(); Caretaker c = new Caretaker(); //改變負責人對象的狀態 o.setState("On"); //建立備忘錄對象,並將發起人對象的狀態儲存起來 c.saveMemento(o.createMemento()); //修改發起人的狀態 o.setState("Off"); //恢複發起人對象的狀態 o.restoreMemento(c.retrieveMemento()); System.out.println(o.getState()); }}
在上面的這個示意性的用戶端角色裡面,首先將發起人對象的狀態設定成“On”,並建立一個備忘錄對象將這個狀態儲存起來;然後將發起人對象的狀態改成“Off”;最後又將發起人對象恢複到備忘錄對象所儲存起來的狀態,即“On”狀態。
系統的時序圖更能夠反映出系統各個角色被調用的時間順序。如是將發起人對象的狀態儲存到白箱備忘錄對象中去的時序圖。
可以看出系統啟動並執行時序是這樣的:
(1)將發起人對象的狀態設定成“On”。
(2)調用發起人角色的createMemento()方法,建立一個備忘錄對象將這個狀態儲存起來。
(3)將備忘錄Object Storage Service到負責人對象中去。
將發起人對象恢複到備忘錄對象所記錄的狀態的時序圖如下所示:
可以看出,將發起人對象恢複到備忘錄對象所記錄的狀態時,系統的運行時序是這樣的:
(1)將發起人狀態設定成“Off”。
(2)將備忘錄對象從負責人對象中取出。
(3)將發起人對象恢複到備忘錄對象所儲存起來的狀態,即“On”狀態。
“黑箱”備忘錄模式的實現
備忘錄角色對發起人(Originator)角色對象提供一個寬介面,而為其他對象提供一個窄介面。這樣的實現叫做“黑箱實現”。
在JAVA語言中,實現雙重介面的辦法就是將備忘錄角色類設計成發起人角色類的內部成員類。
將Memento設成Originator類的內部類,從而將Memento對象封裝在Originator裡面;在外部提供一個標識介面MementoIF給Caretaker以及其他對象。這樣,Originator類看到的是Menmento的所有介面,而Caretaker以及其他對象看到的僅僅是標識介面MementoIF所暴露出來的介面。
使用內部類實現備忘錄模式的類圖如下所示。