所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
个人理解
备忘录模式是用于将对象的状态暂存在某些特殊情况下可以将其进行恢复的模式,可以通过多种方式实现,包括clone以及一般方式以及多种参数的备忘录等形式。标准的备忘录在项目中很难直接应用进去,多数为其变形后的处理方式。
备忘录模式角色
备忘录模式主要包含入下几个角色:
Originator: 原发器。负责创建一个备忘录,用以记录当前对象的内部状态,通过也可以使用它来利用备忘录恢复内部状态。同时原发器还可以根据需要决定Memento存储Originator的那些内部状态。
Memento: 备忘录。用于存储Originator的内部状态,并且可以防止Originator以外的对象访问Memento。在备忘录Memento中有两个接口,其中Caretaker只能看到备忘录中的窄接口,它只能将备忘录传递给其他对象。Originator可以看到宽接口,允许它访问返回到先前状态的所有数据。
Caretaker: 负责人。负责保存好备忘录,不能对备忘录的内容进行操作和访问,只能够将备忘录传递给其他对象。
案例解析
案例一:一般备忘录模式
当对象中只有一个参数的时候,使用备忘录模式,保存下当前的状态,交由备忘录类进行暂存,而备忘录对象交由管理员进行管理,当需要进行恢复的时候,直接使用备忘录的管理员进行获取备忘录,设置当前要恢复的对象的属性值就可以了。
主要代码
备忘录的管理员
public class CareTaker { private Memento memento; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }
备忘录类
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 Originator { private String state = ""; public String getState() { return this.state; } public void setState(String state) { this.state = state; } public Memento createMemento() { return new Memento(this.state); } public void backState(Memento memento) { this.setState(memento.getState()); } }
测试类
public class MainTest { public static void main(String[] args) { Originator orig = new Originator(); orig.setState("很好"); System.out.println(orig.getState()); CareTaker taker = new CareTaker(); taker.setMemento(orig.createMemento()); orig.setState("不好"); System.out.println(orig.getState()); orig.backState(taker.getMemento()); System.out.println(orig.getState()); } }
案例二:clone实现备忘录模式
将原发器角色和备忘录角色融合为一个角色,通过克隆对象本身实现备份对象的目的。当需要恢复的时候,将克隆的对象的副本中的值取出并进行设置操作就可以了。
主要代码
public class Originator implements Cloneable { private Originator orignator; private String state = ""; public String getState() { return this.state; } public void setState(String state) { this.state = state; } public void createMemento() { orignator = this.clone(); } public void backState() { this.setState(orignator.getState()); } @Override protected Originator clone() { try { return (Originator) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } }
测试类
public class MainTest { public static void main(String[] args) { Originator orig = new Originator(); orig.setState("很好"); System.out.println(orig.getState()); orig.createMemento(); orig.setState("不好"); System.out.println(orig.getState()); orig.backState(); System.out.println(orig.getState()); } }
案例三:多种参数的情况下备忘录模式
本例中通过反射技术,获取参数信息,调用其set和get方法分别来设置和获取对象中参数的值,将对象的属性值,封装到HashMap中去,备份的时候通过get读取其值,存储到HashMap中去,回滚的时候将存储的HashMap中的值通过set方法将值设置到原来的属性中去。
此外,备忘录管理员角色,实现了支持版本的备忘录恢复,可以根据具体的需要进行恢复,即:允许恢复到之前的某一个版本。
主要代码
备忘录管理员角色类
public class Caretaker { private Map<String, Memento> mementoMap = new HashMap<String, Memento>(); public Map<String, Memento> getMementoMap() { return mementoMap; } public void setMementoMap(Map<String, Memento> mementoMap) { this.mementoMap = mementoMap; } }
备忘录角色类
public class Memento { private HashMap<String, Object> stateMap; public Memento(HashMap<String, Object> stateMap) { this.stateMap = stateMap; } public HashMap<String, Object> getStateMap() { return stateMap; } public void setStateMap(HashMap<String, Object> stateMap) { this.stateMap = stateMap; } }
发起人角色类
public 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.store(this)); } public void backState(Memento memento) { BeanUtils.back(this, memento.getStateMap()); } @Override public String toString() { return "Originator [state1=" + state1 + ", state2=" + state2 + ", state3=" + state3 + "]"; } }
类属性值得存储于恢复辅助类
public class BeanUtils { public static HashMap<String, Object> store(Object obj) { HashMap<String, Object> stateMap = new HashMap<String, Object>(); try { BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass()); PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor proper : properties) { String propertyName = proper.getName(); Method readMethod = proper.getReadMethod(); Object filedValue = readMethod.invoke(obj, new Object[]{}); if(!propertyName.equalsIgnoreCase("class")) stateMap.put(propertyName, filedValue); } } catch (Exception e) { e.printStackTrace(); } return stateMap; } public static void back(Object obj, HashMap<String, Object> stateMap) { try { BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass()); PropertyDescriptor[] propers = beanInfo.getPropertyDescriptors(); for (PropertyDescriptor proper : propers) { String properName = proper.getName(); Method method = proper.getWriteMethod(); if(stateMap.containsKey(properName)) { method.invoke(obj, stateMap.get(properName)); } } } catch (Exception e) { e.printStackTrace(); } } }
测试类
public class MainTest { public static void main(String[] args) { Originator orig = new Originator(); orig.setState1("很好"); orig.setState2("很不好"); orig.setState3("很好"); HashMap<String, Memento> mementoMap = new HashMap<String, Memento>(); Caretaker taker = new Caretaker(); mementoMap.put("001", orig.createMemento()); orig.setState1("很好"); orig.setState2("很好"); orig.setState3("很好"); mementoMap.put("002", orig.createMemento()); taker.setMementoMap(mementoMap); orig.backState(taker.getMementoMap().get("001")); System.out.println(orig); orig.backState(taker.getMementoMap().get("002")); System.out.println(orig); } }
备忘录模式优点
1、 给用户提供了一种可以恢复状态的机制。可以是用户能够比较方便地回到某个历史的状态。
2、 实现了信息的封装。使得用户不需要关心状态的保存细节。
备忘录模式缺点
消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
备忘录模式应用场景
1、 需要保存一个对象在某一个时刻的状态或部分状态。
2、 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过负责人可以间接访问其内部状态。