备忘录模式
备忘录模式是一个类的状态的恢复,由于单一职责,所以这功能不放在类自己内部,而是单独列出来,然后类内部持有一份备忘录对象。然后还要一个管理备忘录的对象,让外部场景类调用。
定义是“在不破坏封装性的前提下,捕获一个对象的内部状 态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。”
代码语言:javascript复制public class Originator { // 要用到备忘录恢复 state 的类
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());
}
}
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 Client { // 场景类
public static void main(String[] args) {
Originator originator = new Originator();
Memento mem = originator.createMemento();
originator.restoreMemento(mem);
}
}
然后书上说这不符合迪米特原则呀,作为场景类 Client,就跟 Originator 打交道就行了,干嘛还要自己去管理备忘录 Memento 呢?到这我还能理解,然后说要新建一个管理者
代码语言:javascript复制public 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();
Caretaker caretaker = new Caretaker();
caretaker.setMemento(originator.createMemento());
originator.restoreMemento(caretaker.getMemento());
}
}
这我没懂,加了一层到底优化在哪儿了,不和 Memento 打交道了,但你不是又何 Caretaker 产生耦合了吗?Caretaker 比 Memento 高贵在哪了?
原型模式实现的备忘录
代码语言:javascript复制public class Originator implements Cloneable{
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//创建一个备忘录,不再单独创建备忘录对象
public Originator createMemento(){
return this.clone();
}
//恢复一个备忘录
public void restoreMemento(Originator _originator){
this.setState(_originator.getState());
}
//克隆当前对象
@Override
protected Originator clone(){
try {
return (Originator)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
// 到这他又把 Caretaker 去了
public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
originator.createMemento();
originator.restoreMemento();
}
}
用 clone 状态在自己内部,没有通过单独一个对象去保存。因为涉及浅拷贝问题,所以类复杂时可能会导致这种方法也变得很复杂,所以说用在简单的场合。
多属性备忘
通过 Java 的 Introspector 类获取所有属性和值,然后存储到 HashMap 中。
代码语言:javascript复制public class Originator { //内部状态
private String state1 = "";
private String state2 = "";
private String state3 = "";
// getter/setter
//创建一个备忘录
public Memento createMemento(){
return new Memento(BeanUtils.backupProp(this));
}
//恢复一个备忘录
public void restoreMemento(Memento _memento){
BeanUtils.restoreProp(this, _memento.getStateMap());
}
}
public class BeanUtils {
public static HashMap<String,Object> backupProp(Object bean){
HashMap<String,Object> result = new HashMap<String,Object>();
try {
//获得Bean描述
BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass()); //获得属性描述
PropertyDescriptor[] descriptors=beanInfo.getPropertyDescrip //遍历所有属性
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) { //异常处理
}
return result;
} //把HashMap的值返回到bean中
public static void restoreProp(Object bean,HashMap<String,Object> propMap){ try {
//获得Bean描述
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) { //异常处理
System.out.println("shit");
e.printStackTrace();
}
}
public class Memento { //接受HashMap作为状态
private HashMap<String,Object> stateMap;
public Memento(HashMap<String,Object> map){
this.stateMap = map;
}
// getter/setter
}
多备份
到了需要多备份,感觉是需要一个 Caretaker 了,Client 去管理就不好了。
代码语言:javascript复制public class Caretaker {
// 多个备份,时间戳作为键
private HashMap<String,Memento> memMap = new HashMap<String,Memento>();
public Memento getMemento(String idx) {
return memMap.get(idx);
}
public void setMemento(String idx,Memento memento) {
this.memMap.put(idx, memento);
}
}
public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
//创建两个备忘录
caretaker.setMemento("001",originator.createMemento());
caretaker.setMemento("002",originator.createMemento());
//恢复一个指定标记的备忘录
originator.restoreMemento(caretaker.getMemento("001"));
}
}
备份一旦产生就会装入内存,有可能产生内存溢出。所以严格限制备忘录的创建。
权限
备份不许篡改,权限要小。将原来的 Memento 写到 Originator 内部去,作为私有的内部类,让它实现一个空接口 IMemento,这样只有 Originator 能读写,而外面的其它交互都用接口,而由于接口是空的,就是用反射也没什么方法可以调用。