備忘錄模式提供了一種對象狀態的撤銷實現機制,當系統中某一對象需要恢復到某一歷史狀態時可以使用備忘錄模式來進行設計 模式動機 人人都有後悔的時候,在軟體使用過程中難免會出現一些誤操作,如不小心刪除了某些文字或圖片,數據填入錯誤等,對於這些誤操作,需要提供一種後悔藥機制,讓系統可以回到誤操作前的狀態,這 ...
備忘錄模式提供了一種對象狀態的撤銷實現機制,當系統中某一對象需要恢復到某一歷史狀態時可以使用備忘錄模式來進行設計
模式動機
人人都有後悔的時候,在軟體使用過程中難免會出現一些誤操作,如不小心刪除了某些文字或圖片,數據填入錯誤等,對於這些誤操作,需要提供一種後悔藥機制,讓系統可以回到誤操作前的狀態,這就是備忘錄模式的模式動機
模式定義
在不破壞封裝的前提下,捕獲一個對象的內部狀態,併在該對象之外保存這個狀態,這樣可以在以後將對象恢復到原先保存的狀態。備忘錄模式是一種對象行為型模式,其別名為 Token
模式結構
-
Originator(原發器)
原發器可以創建一個備忘錄,並存儲它的當前內部狀態,也可以使用備忘錄來恢復其內部狀態。一般將需要保存內部狀態的類設計為原發器。
-
Memento(備忘錄)
存儲原發器的內部狀態,根據原發器來決定保存哪些內部狀態。需要註意的是,除了原發器本身與負責人之外,備忘錄對象不能直接供其他類使用。
-
Caretaker(負責人)
負責人又稱管理者,它負責保存備忘錄,但是不能對備忘錄的內容進行操作或檢查。
模式分析
前面已經說了,備忘錄模式就是用來吃後悔藥的,理解起來並不難,關鍵在於如何設計備忘錄類和負責人類。
備忘錄中存儲的是原發器的中間狀態,因此需要防止原發器以外的其他對象訪問備忘錄。也不能在備忘錄對象之外保存原發器狀態,如果暴露其內部狀態將違反封裝的原則。
為了實現對備忘錄對象的封裝,需要對備忘錄的調用進行控制。對於原發器而言,它可以調用備忘錄的所有信息,允許原發器訪問先前狀態的所有數據。對於負責人而言,只負責備忘錄的保存並將備忘錄傳遞給其他對象。對於其他對象而言,只需要從負責人處取出備忘錄對象並將原發器對象的狀態恢復,而無須關心備忘錄的保存狀態。
下麵通過一個實例來進一步理解備忘錄模式。
模式實例
某系統提供了用戶信息操作模塊,用戶可以修改自己的各項信息,用戶在進行了錯誤操作後可以恢復到操作之前的狀態。
-
原發器 UserInfoDTO(用戶信息類)
package dp.memento; public class UserInfoDTO { private String account; private String password; public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } // 創建一個備忘錄對象 public Memento saveMemento() { return new Memento(account, password); } // 根據備忘錄對象恢複原發器狀態 public void restoreMemento(Memento memento) { this.account = memento.getAccount(); this.password = memento.getPassword(); } public void show() { System.out.println("Account: " + this.account); System.out.println("Password: " + this.getPassword()); } }
-
備忘錄 Memento
設計備忘錄時需要考慮到封裝性,即除了原發器類,不允許其他類來調用其構造函數與相關方法。一般將備忘錄類和原發器類定義在同一包中來實現封裝,使用預設訪問標識符來定義備忘錄類,即保證包內可見性。
package dp.memento; class Memento { private String account; private String password; public Memento(String account, String password) { this.account = account; this.password = password; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
-
負責人 Caretaker
package dp.memento; public class Caretaker { private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }
-
客戶端測試類 Client
import dp.memento.UserInfoDTO; import dp.memento.Caretaker; public class Client { public static void main(String[] args) { UserInfoDTO user = new UserInfoDTO(); // 創建負責人 Caretaker caretaker = new Caretaker(); user.setAccount("zhangsan"); user.setPassword("123456"); System.out.println("狀態一"); user.show(); // 保存備忘錄 caretaker.setMemento(user.saveMemento()); System.out.println("------------------------"); user.setPassword("11111111"); System.out.println("狀態二"); user.show(); System.out.println("------------------------"); // 從備忘錄中恢復 user.restoreMemento(caretaker.getMemento()); System.out.println("回到狀態一"); user.show(); System.out.println("------------------------"); } }
-
運行結果
模式優缺點
備忘錄模式優點:
- 提供了一種狀態恢復的實現機制,使得用戶可以方便地回到特定的一個歷史步驟
- 實現了信息的封裝,備忘錄只保存原發器的狀態,不會被其他代碼修改。採用堆棧來存儲備忘錄對象可以實現多次撤銷操作,可以通過在負責人中定義集合對象來存儲多個備忘錄。
備忘錄模式缺點:
- 每保存一次對象的狀態都需要消耗記憶體資源,資源消耗過大
模式適用環境
在以下情況可以使用備忘錄模式:
- 保存一個對象在某一時刻的狀態或部分狀態
- 如果用一個介面來讓其他對象得到這些狀態,將會暴露對象的實現細節並破壞封裝性,一個對象不希望外界直接訪問得到其內部狀態,通過負責人可以間接訪問其內部狀態