七、備忘錄模式Memento(行為型模式)
其目的是,在不違反封裝原則的前提下.採集和備份一個對象的內部狀態以便這個對象能夠在以後恢復到之前的某個狀態.
備忘錄模式的操作過程?
1、client為發起人角色創建一個備忘錄對象。
?
2、調用發起人對象的某個操作。這個操作是能夠撤銷的。
?
3、檢查發起人對象所出狀態的有效性。檢查的方式能夠是發起人對象的內部自查。也能夠由某個外部對象進行檢查。?
4、假設須要的話。將發起人的操作撤銷。也就是說依據備忘錄對象的記錄。將發起人對象的狀態恢復過來。?
“假如”協議模式的操作過程:?
1、將發起人對象做一個拷貝。
?
2、在拷貝上運行某個操作。
?
3、檢查這個拷貝的狀態是否有效和自恰。?
4、假設檢查結果是無效或者不自恰的,那麽扔掉這個拷貝,並觸發異常處理程序。相反。假設檢查是有效和自恰的,那麽在原對象上運行這個操作?
顯然這一做法對於撤銷一個操作並恢復操作前狀態較為復雜和困難的發起人對象來說是一個較為慎重和有效的做法。?
“假如”協議模式的長處和缺點?
詳細來說,這個做法的好處是能夠保證發起人對象永遠不會處於無效或不自恰的狀態上。這樣作的短處是成功的操作必須運行兩次。?
假設操作的成功率較低的話。這樣做就比較劃算。反之就不太劃算。
?
使用備忘錄模式的長處和缺點?
一、備忘錄模式的長處?
1、有時一些發起人對象的內部信息必須保存在發起人對象以外的地方,可是必須要由發起人對象自己讀取,這時,?
?? 使用備忘錄模式能夠把復雜的發起人內部信息對其它的對象屏蔽起來。從而能夠恰當地保持封裝的邊界。?
2、本模式簡化了發起人類。發起人不再須要管理和保存其內部狀態的一個個版本號,client能夠自行管理他們所需?
?? 要的這些狀態的版本號。
?
3、當發起人角色的狀態改變的時候,有可能這個狀態無效。這時候就能夠使用臨時存儲起來的備忘錄將狀態復原。?
二、備忘錄模式的缺點:?
1、假設發起人角色的狀態須要完整地存儲到備忘錄對象中。那麽在資源消耗上面備忘錄對象會非常昂貴。
?
2、當負責人角色將一個備忘錄 存儲起來的時候,負責人可能並不知道這個狀態會占用多大的存儲空間,從而無法?
?? 提醒用戶一個操作是否非常昂貴。882——P ?
3、當發起人角色的狀態改變的時候,有可能這個協議無效。
假設狀態改變的成功率不高的話,不如採取“假如”協議模式。
(1)寬接口和白箱:
public class Client { private static Originator originator = new Originator(); private static Caretaker c = new Caretaker(); public static void main(String[] args) { // 該發起人對象的狀態 originator.setState("On"); // 創建備忘錄對象,並將發起人對象的狀態存儲起來 c.saveMemento(originator.createMemento()); // 改動發起人對象的狀態 originator.setState("Off"); // 恢復發起人對象的狀態 originator.restoreMemento(c.retrieveMemento()); } }
// 發起人角色 class Originator { private String state; // 工廠方法,返還一個新的備忘錄對象 public Memento createMemento() { return new Memento(state); } // 將發起人恢復到備忘錄對象所記載的狀態 public void restoreMemento(Memento memento) { this.state = memento.getState(); } // 狀態的取值方法 public String getState() { return this.state; } // 狀態的賦值方法 public void setState(String state) { this.state = state; System.out.println("Current state = " + this.state); } }
/* * 備忘錄模式要求備忘錄對象提供兩個不同的接口:一個寬接口提供給發起人對象,還有一個窄接口提供給全部其它的對象,包含負責人對象。* 寬接口同意發起人讀取到全部的數據。窄接口僅僅同意它把備忘錄對象傳給其它的對象而看不到內部的數據。 */ // 備忘錄角色 class Memento { private String state; public Memento(String state) { this.state = state; } public String getState() { return this.state; } public void setState(String state) { this.state = state; } }
/* * 負責人角色負責保存備忘錄對象,可是從不改動(甚至不查看)備忘錄對象的內容(一個更好的實現是負責人對象根本無法從備忘錄 對象中讀取個改動其內容) */ // 負責人角色 class Caretaker { private Memento memento; // 備忘錄的取值方法 public Memento retrieveMemento() { return this.memento; } // 備忘錄的賦值方法 public void saveMemento(Memento memento) { this.memento = memento; } }
首先將發起人對象的狀態設置成“On”(或者不論什麽有效狀態),而且創建一個備忘錄對象將這個狀態存儲起來。然後將發起人對象的狀態改成“Off”(或者不論什麽狀態);最後又將發起人對象恢復到備忘錄對象所存儲起來的狀態,即“On”狀態(或者先前所存儲的不論什麽狀態)
備忘錄系統運行的時序是這種:
(1)將發起人對象的狀態設置成“On”。
(2)調用發起人角色的createMemento()方法,創建一個備忘錄對象將這個狀態存儲起來。
(3)將備忘錄對象存儲到負責人對象中去。
備忘錄系統恢復的時序是這種:
(1)將發起人對象的狀態設置成“Off”;
(2)將備忘錄對象從負責人對象中取出;
(3)將發起人對象恢復到備忘錄對象所存儲起來的狀態。“On”狀態。
白箱實現的優缺點
白箱實現的一個明顯的好處是比較簡單,因此經常常使用做教學目的。
白箱實現的一個明顯的缺點是破壞對發起人狀態的封裝。
(2)窄接口或者黑箱實現://client public class Client { private static Originator originator = new Originator(); private static Caretaker c = new Caretaker(); public static void main(String[] args) { // 該發起人對象的狀態 originator.setState("On"); // 創建備忘錄對象。並將發起人對象的狀態存儲起來 c.saveMemento(originator.createMemento()); // 改動發起人對象的狀態 originator.setState("Off"); // 恢復發起人對象的狀態 originator.restoreMemento(c.retrieveMemento()); } }
// 發起人角色 class Originator { private String state; public Originator() { } // 工廠方法,返還一個新的備忘錄對象 public MementoIF createMemento() { return new Memento(this.state); } // 將發起人恢復到備忘錄對象記錄的狀態 public void restoreMemento(MementoIF memento) { Memento aMemento = (Memento) memento; this.setState(aMemento.getState()); } public String getState() { return this.state; } public void setState(String state) { this.state = state; System.out.println("state =" + state); } protected class Memento implements MementoIF { private String savedState; public Memento(String someState) { this.savedState = someState; } private void setState(String someState) { savedState = someState; } private String getState() { return savedState; } } }
interface MementoIF { }
// 備忘錄角色 class Memento implements MementoIF { private String state; public Memento(String state) { this.state = state; } public String getState() { return this.state; } public void setState(String state) { this.state = state; } }
class Caretaker { private MementoIF memento; public MementoIF retrieveMemento() { return this.memento; } public void saveMemento(MementoIF memento) { this.memento = memento; } }
黑箱實現運行時的時序為;
? (1)將發起人對象的狀態設置成“On”。
(2)調用發起人角色的 createMemento()方法。創建一個備忘錄對象將這個狀態存儲起來。
(3)將備忘錄對象存儲到負責人對象中去。因為負責人對象拿到的僅是 MementoIF類型,因此無法讀出備忘錄內部的狀態。
恢復時的時序為:
(1)將發起人對象的狀態設置成“Off”。
(2)將備忘錄對象從負責人對象中取出。
註意此時僅能得到 MementoIF接口,因此無法讀出此對象的內部狀態
(3)將發起人對象的狀態恢復成備忘錄對象所存儲起來的狀態。,因為發起人對象的內部類Memento實現了MementoIF接口
這個內部類是傳入的備忘錄對象的真實類型,因此發起人對象能夠利用內部類Memento 的私有 接口讀出此對象的內部狀態。
(3)存儲多個狀態的備忘錄模式:
//發起人角色 import java.util.Vector; import java.util.Enumeration; public class Originator{ private Vector states; private int index; public Originator(){ states = new Vector(); index = 0; } public Memento createMementor(){ return new Mementor(states,index); } public void restoreMementor(Mementor memento){ states = memento.getStates(); index = memento.getIndex()。 } public void setState(String state){ this.states.addElement(state); index ++; } //輔助方法。打印出全部的狀態 public void printStates(){ System.out.println("Total number of states: " + index); for(Enumeration e = states.elements();e.hasMoreElements();){ system.out.println(e.nextElement()); } } }
//備忘錄角色 import java.util.Vector; public class Memento{ private Vector states; private int index; //<span style="font-size: 14px; font-family: Arial, Helvetica, sans-serif;">備忘錄的構造子克隆了傳入的states,然後將克隆存入到備忘錄對象內部,這是一個重要的細節,因為不這種話,將會</span><pre title="備忘錄(Memento Pattern)模式 【行為模式第一篇】" style="font-size: 14px;"> //將會造成client和備忘錄對象持有對同一個Vector對象的引用,也能夠同一時候改動這個Vector對象,會造成系統崩潰。public Memento(Vector states,int index){this.states = (Vector)states.clone();this.index = index;}//狀態取值方法Vector getStates(){return states;}//檢查點取值方法int getIndex(){return this.index;}}
//負責人角色 import java.util.Vector; public class Caretaker{ private Originator o; private Vector mementos = new Vector(); private int current; public Caretaker(Originator o){ this.o = o; current = 0; } public int createMemento(){ Memento memento = o.createMemento(); mementos.addElement(memento); return current ++; } //將發起人恢復到某個檢查點 public void restoreMemento(int index){ Memento memento = (Memento)mementos.elementAt(index); o.restoreMemento(memento); } //某個檢查點刪除 public void removeMemento(int index){ mementos.removeElementAt(index); } }
//client public class Client{ private static Originator o = new Originator(); private static Caretaker c = new Caretaker(o); public static void main(String[] args){ //改變狀態 o.setState("state 0"); //建立一個檢查點 c.createMemento(); //改變狀態 o.setState("state 1"); c.createMemento(); o.setState("state 2"); c.createMemento(); o.setState("state 3"); c.createMemento(); o.setState("state 4"); c.createMemento(); o.printStates(); //恢復到第二個檢查點 System.out.println("Restoring to 2"); c.restoreMemento(2); o.printStates(); System.out.println("Restoring to 0"); c.restoreMemento(0); o.printStates(); System.out.println("Restoring to 3"); c.restoreMemento(3); o.printStates(); } }
(4)自述歷史模式(備忘錄模式的一個變種)
因為“自述歷史”作為一個備忘錄模式的特殊實現形式非常easy易懂。它可能是備忘錄模式最為流行的實現形式。
?
//窄接口 public interface MementoIF{}
//發起人角色 public class Originator{ public String state; public Originator(){} public void changeState(String state){ this.state = state; System.out.println("State has been changed to : " + state); } public Memento createMemento(){ return new Memento(this); } public void restoreMemento(MementoIF memento){ Memento m = (Memento)memento; changeState(m.state); } class Memento implements MementoIF{ private String state; private String getState(){ return state; } private Memento(Originator o){ this.state = o.state; } } }
//client public class Client{ private static Originator o; private static MementoIF memento; public static void main(String args[]){ o = new Originator(); o.changeState("State 1"); memento = o.createMemento(); o.changeState("State 2"); o.restoreMemento(memento); } }
七、備忘錄模式Memento(行為型模式)