在设计模式系列文章是阅读《设计模式之禅》之后自己新的加上在项目中的应用于思考,想要系统深入学习的朋友还是建议学习一下《设计模式之禅》,这本书中通过例子讲的更加循序渐进,也更容易理解。
理解理念
这种模式旨在让程序能够记住一个或多个实体的属性或历史状态,并且能够进行还原操作。这种问题的复杂性通常来自于“多”这个方面。如果我们只需要备份一个属性,并且只需要备份最近的状态,那么我们只需要创建一个与该属性相同的变量。同样地,如果我们只需要备份少量对象并且对象属性也很少,那么手动实现备份可能是可行的。但是,如果项目中存在多个需要备份的对象,并且这些对象的属性也很多,那么我们需要使用工具来实现备份操作。因此,我们抽象出了这种模式来帮助解决这类问题。
代码实现
对于上述复杂场景,有两种备份实现思路。一种是让对象自己实现创建备份的接口,另一种是专门创建一个对象来进行备份操作。在实现的细节方面,如果对象内部只有基本数据类型的属性,则不需要考虑深拷贝和浅拷贝。但是,如果内部存在引用数据类型的属性,则需要在拷贝赋值时使用深拷贝的方式。为了规范对象的属性和操作,需要在项目中需要进行备份的对象中实现统一的克隆接口(自定义一个备份接口并实现克隆方法)。这样,我们也可以在备份对象时进行检测和区分。
public class BeanUtils {public class BeanUtils {public static HashMap<String, Object> backupPro(Object bean){HashMap<String,Object> result = new HashMap<>();try{BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();for (PropertyDescriptor des : propertyDescriptors) {String filedName = des.getName();Method getter = des.getReadMethod();Object filedValue = getter.invoke(bean,new Object[]{});if (!filedName.equalsIgnoreCase("class")){result.put(filedName,filedValue);}}}catch (Exception e){System.out.println("程序自定义的异常处理");}return result;}public static void restorePro(Object bean,HashMap<String,Object> propMap){try{BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();for (PropertyDescriptor des : propertyDescriptors){String filedName = des.getName();if (propMap.containsKey(filedName)){Method setter = des.getWriteMethod();setter.invoke(bean,new Object[]{propMap.get(filedName)});}}}catch (Exception e){System.out.println("程序自定义的异常处理");}}
} public class Caretaker {/*** 双层Map是考虑建立以时间戳为维度的多个备份,结合具体场景也可以设计成队列* 或是指定版本号的备忘录亦或是先进先出的队列等,同时可以控制具体的大小*/private Map<Long,Map<String,Object>> mementoCollection = new HashMap<>();public Long createMemento(Object bean){long timestamp = System.currentTimeMillis();mementoCollection.put(timestamp,BeanUtils.backupPro(bean));return timestamp;}public void restoreBean(Long version,Object bean){BeanUtils.restorePro(bean, (HashMap<String, Object>) mementoCollection.get(version));}
}public class Originator{private String state1;private String state2;public Originator(){}public Originator(String state1, String state2) {this.state1 = state1;this.state2 = state2;}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;}@Overridepublic String toString() {return "state1='" + state1 + ", state2='" + state2 + '\'';}
}public class Client {public static void main(String[] args) {Originator originator = new Originator("state1a","state2a");Caretaker caretaker = new Caretaker();Long mementoVersion = caretaker.createMemento(originator);originator.setState2("state2b");System.out.println(originator.toString());caretaker.restoreBean(mementoVersion,originator);System.out.println(originator.toString());}
}
项目应用
在内部项目——快速取数,一个查询进来会有一个QueryContext,里面记录着查询的状态信息以及上线文信息,利用设计模式里面可以在关键节点对QueryContext进行备份,当需要返回到某一个步骤进行操作的时候我们就获取指定版本的备份,让程序在指定入口重新执行。这样避免了程序从头执行的不必要成本——前面的已经执行过了那就也没必要再执行了。这样不仅能节省资源而且也可以为用户提供更多的选择以及更高的使用体验——重新执行从错误的地方开始好了。