定義:在不破壞封裝性的前提下(xià),捕獲一(yī)個對象的内部狀态,并在該對象之外(wài)保存這個狀态。這樣就可以将該對象恢複到原先保存的狀态
類型:行爲類
類圖:
我(wǒ)們在編程的時候,經常需要保存對象的中(zhōng)間狀态,當需要的時候,可以恢複到這個狀态。比如,我(wǒ)們使用Eclipse進行編程時,假如編寫失誤(例如不小(xiǎo)心誤删除了幾行代碼),我(wǒ)們希望返回删除前的狀态,便可以使用Ctrl+Z來進行返回。這時我(wǒ)們便可以使用備忘錄模式來實現。
備忘錄模式的結構
發起人:記錄當前時刻的内部狀态,負責定義哪些屬于備份範圍的狀态,負責創建和恢複備忘錄數據。
備忘錄:負責存儲發起人對象的内部狀态,在需要的時候提供發起人需要的内部狀态。
管理角色:對備忘錄進行管理,保存和提供備忘錄。
通用代碼實現
class Originator { private String state = ""; public String getState() { return state; } public void setState(String state) { this.state = state; } public Memento createMemento(){ return new Memento(this.state); } public void restoreMemento(Memento memento){ this.setState(memento.getState()); } } 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; } } class Caretaker { private Memento memento; public Memento getMemento(){ return memento; } public void setMemento(Memento memento){ this.memento = memento; } } public class Client { public static void main(String[] args){ Originator originator = new Originator(); originator.setState("狀态1"); System.out.println("初始狀态:"+originator.getState()); Caretaker caretaker = new Caretaker(); caretaker.setMemento(originator.createMemento()); originator.setState("狀态2"); System.out.println("改變後狀态:"+originator.getState()); originator.restoreMemento(caretaker.getMemento()); System.out.println("恢複後狀态:"+originator.getState()); } }
代碼演示了一(yī)個單狀态單備份的例子,邏輯非常簡單:Originator類中(zhōng)的state變量需要備份,以便在需要的時候恢複;Memento類中(zhōng),也有一(yī)個state變量,用來存儲Originator類中(zhōng)state變量的臨時狀态;而Caretaker類就是用來管理備忘錄類的,用來向備忘錄對象中(zhōng)寫入狀态或者取回狀态。
多狀态多備份備忘錄
通用代碼演示的例子中(zhōng),Originator類隻有一(yī)個state變量需要備份,而通常情況下(xià),發起人角色通常是一(yī)個javaBean,對象中(zhōng)需要備份的變量不止一(yī)個,需要備份的狀态也不止一(yī)個,這就是多狀态多備份備忘錄。實現備忘錄的方法很多,備忘錄模式有很多變形和處理方式,像通用代碼那樣的方式一(yī)般不會用到,多數情況下(xià)的備忘錄模式,是多狀态多備份的。其實實現多狀态多備份也很簡單,最常用的方法是,我(wǒ)們在Memento中(zhōng)增加一(yī)個Map容器來存儲所有的狀态,在Caretaker類中(zhōng)同樣使用一(yī)個Map容器才存儲所有的備份。下(xià)面我(wǒ)們給出一(yī)個多狀态多備份的例子:
class Originator { private String state1 = ""; private String state2 = ""; private String state3 = ""; public String getState1() { return state1; } public void setState1(String state1) { this.state1 = state1; } public String getState2() { return state2; } public void setState2(String state2) { this.state2 = state2; } public String getState3() { return state3; } public void setState3(String state3) { this.state3 = state3; } public Memento createMemento(){ return new Memento(BeanUtils.backupProp(this)); } public void restoreMemento(Memento memento){ BeanUtils.restoreProp(this, memento.getStateMap()); } public String toString(){ return "state1="+state1+"state2="+state2+"state3="+state3; } } class Memento { private Map stateMap; public Memento(Map map){ this.stateMap = map; } public Map getStateMap() { return stateMap; } public void setStateMap(Map stateMap) { this.stateMap = stateMap; } } class BeanUtils { public static Map backupProp(Object bean){ Map result = new HashMap(); try{ BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); for(PropertyDescriptor des: descriptors){ String fieldName = des.getName(); Method getter = des.getReadMethod(); Object fieldValue = getter.invoke(bean, new Object[]{}); if(!fieldName.equalsIgnoreCase("class")){ result.put(fieldName, fieldValue); } } }catch(Exception e){ e.printStackTrace(); } return result; } public static void restoreProp(Object bean, Map propMap){ try { BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); for(PropertyDescriptor des: descriptors){ String fieldName = des.getName(); if(propMap.containsKey(fieldName)){ Method setter = des.getWriteMethod(); setter.invoke(bean, new Object[]{propMap.get(fieldName)}); } } } catch (Exception e) { e.printStackTrace(); } } } class Caretaker { private Map memMap = new HashMap(); public Memento getMemento(String index){ return memMap.get(index); } public void setMemento(String index, Memento memento){ this.memMap.put(index, memento); } } class Client { public static void main(String[] args){ Originator ori = new Originator(); Caretaker caretaker = new Caretaker(); ori.setState1("中(zhōng)國"); ori.setState2("強盛"); ori.setState3("繁榮"); System.out.println("===初始化狀态===n"+ori); caretaker.setMemento("001",ori.createMemento()); ori.setState1("軟件"); ori.setState2("架構"); ori.setState3("優秀"); System.out.println("===修改後狀态===n"+ori); ori.restoreMemento(caretaker.getMemento("001")); System.out.println("===恢複後狀态===n"+ori); } }
備忘錄模式的優缺點和适用場景
備忘錄模式的優點有:
當發起人角色中(zhōng)的狀态改變時,有可能這是個錯誤的改變,我(wǒ)們使用備忘錄模式就可以把這個錯誤的改變還原。
備份的狀态是保存在發起人角色之外(wài)的,這樣,發起人角色就不需要對各個備份的狀态進行管理。
備忘錄模式的缺點:
在實際應用中(zhōng),備忘錄模式都是多狀态和多備份的,發起人角色的狀态需要存儲到備忘錄對象中(zhōng),對資(zī)源的消耗是比較嚴重的。
如果有需要提供回滾操作的需求,使用備忘錄模式非常适合,比如jdbc的事務操作,文本編輯器的Ctrl+Z恢複等。