備忘錄模式屬於三種設計模式中的行爲型模式(另外兩種是建立型模式和結構型模式)。java
定義:在不破壞封閉性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。這樣之後就能夠將改變了的對象恢復到原先保存的狀態。設計模式
之因此須要存儲改變前的對象,是由於java對象的傳遞是引用傳遞。A a1=a,a裏面的東西變就等同於a1裏面的東西變。
架構
備忘錄模式的結構:ide
發起人:負責記錄當前時刻的內部狀態,負責定義那些屬於備忘範圍的狀態,負責建立和恢復備忘錄數據。測試
備忘錄:負責存儲發起人對象的內部狀態,在須要的時候提供發起人須要的內部狀態。this
管理角色:對備忘錄進行管理,保存和提供備忘錄。spa
優勢:設計
當發起人角色中的狀態改變時,有可能這是個錯誤的改變,咱們使用備忘錄模式就能夠還原。rest
備份的狀態是保存在發起人角色以外的,這樣,發起人角色就不須要對哥哥備份狀態進行管理。代碼規範
若是有須要提供回滾操做的需求,使用備忘錄模式很是合適
缺點:
在實際應用中,備忘錄模式多事多狀態和多備份的,發起人角色的狀態須要存儲到備忘錄對象中,對字段的小號是比較嚴重的。
從設計模式角度講:我的感受他對六大設計模式原則的體現不是很大,只能說知足單一職責原則把,好比一個類的實例若是想要從不能備忘改成須要備忘,經過該模式,咱們仍是要爲該類增長一個保存和恢復的方法,違背了開閉原則,client端調用的時候須要知道。爲何不直接定義一個容器,拷貝一個該對象,須要回覆的時候再賦值過去呢(這樣貌似又不三不四了,由於體現不出來備忘是某個對象的能力,略糾結啊)。(歡迎大神透徹分析下)
簡單版測試用例:
public class BeiwangluTest { public static void main(String[] args) { Originator originator=new Originator(); originator.setState("狀態1"); Caretaker caretaker=new Caretaker(); caretaker.setMemento(originator.createMemento()); originator.setState("狀態2"); originator.restorMemento(caretaker.getMemento()); System.out.println(originator.getState());;//這是以前的狀態 originator.createMemento();//這是以前的memento } } /** * 發起ren * @author 58 * */ 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 restorMemento(Memento memento){ this.setState(memento.getState()); } } /** * 備忘錄 * @author 58 * */ 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; } } /** * 管理角色 * @author 58 * */ class Caretaker{ private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }
上述代碼只作到恢復了一個狀態,通常狀況下咱們須要恢復的是一個複雜的對象。
最經常使用的方法是在memento中增長一個map容器來存儲全部的狀態,在caretaker類中一樣適用一個map容器存儲全部備份。下面代碼以下(應該也能夠經過深拷貝實現,經過beanutils來實現會更更簡單,有時間的話試試)
(話外代碼規範:寫代碼時名字要起好,在方法中list list=xxx這種就很差,應該表示清楚list是幹嗎的,否則看起來很費勁。簡單命名應該只出如今循環遍歷中,trycatch等代碼常常會給咱們生成一個//TODO註釋,這個註釋不要留,會讓有代碼潔癖的人反感,能夠從eclipese設置裏去掉,要是不知道爲何,本身去百度//TODO,//XXX,//FIXME 這些註釋各自都是幹嗎用的,嚴格要求從我作起)
public class BeiwangluTest { public static void main(String[] args) { Originator ori=new Originator(); Caretaker caretaker=new Caretaker(); ori.setState1("中國"); ori.setState2("強盛"); ori.setState3("繁榮"); System.out.println("狀態1"+ori); caretaker.setMemento("001",ori.createMemento()); ori.setState1("軟件"); ori.setState2("架構"); ori.setState3("優秀"); System.out.println("狀態2"+ori); ori.restorMemento(caretaker.getMemento("001")); System.out.println("回覆後的狀態"+ori); } } /** * 發起人 * * @author 58 * */ class Originator { private String state1 = ""; private String state2 = ""; private String state3 = ""; public Memento createMemento() { return new Memento(BeanUtils.backupProp(this)); } public void restorMemento(Memento memento) { BeanUtils.restoreProp(this, memento.getStateMap()); } 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; } @Override public String toString() { return "Originator [state1=" + state1 + ", state2=" + state2 + ", state3=" + state3 + "]"; } } /** * 備忘錄 * * @author 58 * */ class Memento { private Map<String, Object> stateMap; public Memento(Map<String, Object> map) { this.stateMap = map; } public Map<String, Object> getStateMap() { return stateMap; } public void setStateMap(Map<String, Object> stateMap) { this.stateMap = stateMap; } } /** * 管理角色 * * @author 58 * */ class Caretaker { private Map<String, Memento> meMap = new HashMap<String, Memento>(); public Memento getMemento(String index) { return meMap.get(index); } public void setMemento(String index, Memento memento) { this.meMap.put(index, memento); } private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } } class BeanUtils { public static Map<String, Object> backupProp(Object bean) { Map<String, Object> result = new HashMap<String, Object>(); try { BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); PropertyDescriptor[] descriptors = beanInfo .getPropertyDescriptors(); for (PropertyDescriptor descriptor : descriptors) { String fieldName = descriptor.getName(); Method getter = descriptor.getReadMethod(); // TODO null和new Object[]{}是不同的,null通常是執行抽象方法。 Object fieldValue = getter.invoke(bean, new Object[]{}); if (!fieldName.equalsIgnoreCase("class")) { result.put(fieldName, fieldValue); } } } catch (IntrospectionException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return result; } public static void restoreProp(Object bean, Map<String, Object> propMap) { try { BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass()); PropertyDescriptor[] descriptors = beanInfo .getPropertyDescriptors(); for (PropertyDescriptor descriptor : descriptors) { String fieldName = descriptor.getName(); if (propMap.containsKey(fieldName)) { Method setterMethod = descriptor.getWriteMethod(); setterMethod.invoke(bean, new Object[] { propMap.get(fieldName) }); } } } catch (IntrospectionException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }