声明:文章内容根据大牛博客的内容,自己理解后,给自己做的学习笔记,文末会附上大牛博客地址链接。有需要沟通交流的可加我QQ群:425120333
在生活中,每个人都应该有这样的经历:如果我当初那样做就好了,也不会出现现在的情况。这里的如果怎样都是对当初所选择的做法的后悔,
如果当初多看点书,如果当初都努力下,如果......太多太多的如果了,可是现实世界没有后悔药,过去了就已经成为事实,改变不了。在现实世界
虽然没有后悔药,但是在代码中有,而这也是今天要介绍的主角——备忘录模式。其中又可以分为:标准版备忘录、clone版备忘录、多状态备忘录、
多备份的备忘录、私有备忘录等。
这里先描述下场景,然后将各种情况的备忘录给实现出来。场景:A的同事生日了,A要送份礼物,可是A不知道同事喜欢什么,如果送了同事喜欢的礼物
当然是皆大欢喜,如果送了不怎么喜欢的,A希望可以回到送礼之前的状态,重新选择礼物。这里就是一个很简单的场景就是要保证A一定能
送同事喜欢的礼物,为了保证这一点,只能是在A送错的情况下,能后悔重新送就可以了。
首先是标准版的备忘录:
public class ChooseGift {
public static void main(String[] args) {
APerson person = new APerson();
MementoManager manager = new MementoManager();
System.out.println("未送礼前的状态:" + person.getGiftName());
manager.setMemento(person.createMemento());
person.setGiftName("不喜欢的礼物");
System.out.println("送错了礼物的状态:" + person.getGiftName());
person.restore(manager.getMemento());
System.out.println("发现了送错了礼物,吃下后悔药,回到开始状态 :" + person.getGiftName());
}
}
class APerson {
private String giftName = "礼物未选择";
public String getGiftName() {
return giftName;
}
public void setGiftName(String giftName) {
this.giftName = giftName;
}
public Memento createMemento() {
return new Memento(this.giftName);
}
public void restore(Memento memento) {
this.setGiftName(memento.getGiftName());
}
}
class Memento {
private String giftName;
public Memento(String giftName) {
this.giftName = giftName;
}
public String getGiftName() {
return this.giftName;
}
public void setGiftName(String giftName) {
this.giftName = giftName;
}
}
class MementoManager {
Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
客户端输出:
未送礼前的状态:礼物未选择
送错了礼物的状态:不喜欢的礼物
发现了送错了礼物,吃下后悔药,回到开始状态 :礼物未选择
clone版备忘录:
public class ChooseGift {
public static void main(String[] args) {
APerson person = new APerson();
System.out.println("未送礼前的状态:" + person.getGiftName());
person.createMemento();
person.setGiftName("不喜欢的礼物");
System.out.println("送错了礼物的状态:" + person.getGiftName());
person.restore();
System.out.println("发现了送错了礼物,吃下后悔药,回到开始状态 :" + person.getGiftName());
}
}
class APerson implements Cloneable {
private APerson person;
private String giftName = "礼物未选择";
public String getGiftName() {
return giftName;
}
public void setGiftName(String giftName) {
this.giftName = giftName;
}
public void createMemento() {
this.person = this.clone();
}
public void restore() {
this.setGiftName(person.getGiftName());
}
@Override
protected APerson clone() {
try {
return (APerson) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
控制台输出:
未送礼前的状态:礼物未选择
送错了礼物的状态:不喜欢的礼物
clone版备忘录模式相比较于标准版代码是简洁了一些,不过其中有两点问题,一是、clone是整个对象的复制,而有时,只需保存一两个状态值,
没必要存储整个对象。二是、在使用clone时,需注意下深克隆和浅克隆的情况。(参考链接:http://blog.csdn.net/hanlipenghanlipeng/article/details/52120741)
私有备忘录:
public class ChooseGift {
public static void main(String[] args) {
APerson person = new APerson();
MementoManager manager = new MementoManager();
System.out.println("未送礼前的状态:" + person.getGiftName());
manager.setMemento(person.createMemento());
person.setGiftName("不喜欢的礼物");
System.out.println("送错了礼物的状态:" + person.getGiftName());
person.restore(manager.getMemento());
System.out.println("发现了送错了礼物,吃下后悔药,回到开始状态 :" + person.getGiftName());
}
}
class APerson {
private String giftName = "礼物未选择";
public String getGiftName() {
return giftName;
}
public void setGiftName(String giftName) {
this.giftName = giftName;
}
public MementoImpl createMemento() {
return new Memento(this.giftName);
}
public void restore(MementoImpl mementoImpl) {
Memento memento = (Memento) mementoImpl;
this.setGiftName(memento.getGiftName());
}
private class Memento implements MementoImpl {
private String giftName;
private Memento(String giftName) {
this.giftName = giftName;
}
private String getGiftName() {
return this.giftName;
}
}
}
interface MementoImpl {
}
class MementoManager {
MementoImpl memento;
public MementoImpl getMemento() {
return memento;
}
public void setMemento(MementoImpl memento) {
this.memento = memento;
}
}
控制台输出:
未送礼前的状态:礼物未选择
送错了礼物的状态:不喜欢的礼物
发现了送错了礼物,吃下后悔药,回到开始状态 :礼物未选择
这里使用了一个新的设计模式:双接口设计,我们设计的一个类可以实现多个接口,在系统设计时,如果考虑对象的安全问题,则可以提供两个接口,
一个是业务的正常接口,实现必要的业务逻辑(平时使用的普通接口),叫做宽接口;另一个接口是空接口,什么方法都没有,其目的是提供给子系统外的模块访问,
比如容器对象,这个叫做窄接口,由于窄接口没有提供任何操纵数据的方法,因此相对来说比较安全。(这一段引自设计模式之禅)
上述描述的都是依赖当前的场景而给出的解决办法,但是需要知道一点是需求是会变的,如果同事要求来的时候顺便带个蛋糕,而A又忘了要到什么口味的,
这时就要求A同时存在两个初始信息,蛋糕信息和礼物信息。虽然可以参考原先的继续构建一个蛋糕的备忘录,但如果后续还增加,那增加的备忘录就太多了,
这时多状态的备忘录就派上用处了。
多状态备忘录模式:
public class ChooseGift {
public static void main(String[] args) {
APerson person = new APerson();
MementoManager manager = new MementoManager();
System.out.println("未送礼前的状态:" + person.getGiftName() + "---" + person.getCakeInfo());
manager.setMemento(person.createMemento());
person.setGiftName("不喜欢的礼物");
person.setCakeInfo("错误口味的蛋糕");
System.out.println("送错了礼物的状态:" + person.getGiftName() + "---" + person.getCakeInfo());
person.restore(manager.getMemento());
System.out.println("发现了送错了礼物,吃下后悔药,回到开始状态 :" + person.getGiftName() + "---" + person.getCakeInfo());
}
}
class APerson {
private String giftName = "礼物未选择";
private String cakeInfo = "蛋糕未选择";
public String getGiftName() {
return giftName;
}
public void setGiftName(String giftName) {
this.giftName = giftName;
}
public String getCakeInfo() {
return cakeInfo;
}
public void setCakeInfo(String cakeInfo) {
this.cakeInfo = cakeInfo;
}
public Memento createMemento() {
Map<String, Object> map = BeanUtil.getFieldInfoMap(this);
return new Memento(map);
}
public void restore(Memento memento) {
this.setGiftName((String) memento.getMap().get("giftName"));
this.setCakeInfo((String) memento.getMap().get("cakeInfo"));
}
}
class Memento {
private Map<String, Object> map;
public Memento(Map<String, Object> map) {
this.map = map;
}
public Map<String, Object> getMap() {
return this.map;
}
}
class MementoManager {
Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
class BeanUtil {
public static Map<String, Object> getFieldInfoMap(Object obj) {
Map<String, Object> map = new HashMap<String, Object>();
String[] fieldNameArray = getFieldNameArray(obj);
for (String fieldName : fieldNameArray) {
map.put(fieldName, getValue(fieldName, obj));
}
return map;
}
/**
* @introduce:根据属性名获取相应的值
*/
private static Object getValue(String fieldName, Object obj) {
try {
String sign = fieldName.substring(0, 1).toUpperCase();
String methodName = "get" + sign + fieldName.substring(1);
Method method = obj.getClass().getMethod(methodName, new Class[] {});
Object object = method.invoke(obj, new Object[] {});
return object;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* @introduce:获取对象的所有属性名
*/
private static String[] getFieldNameArray(Object obj) {
Field[] array = obj.getClass().getDeclaredFields();
String[] fieldNameArray = new String[array.length];
for (int i = 0; i < array.length; i++) {
fieldNameArray[i] = array[i].getName();
}
return fieldNameArray;
}
}
控制台输出:
未送礼前的状态:礼物未选择—蛋糕未选择
送错了礼物的状态:不喜欢的礼物—错误口味的蛋糕
发现了送错了礼物,吃下后悔药,回到开始状态 :礼物未选择—蛋糕未选择
多备份的备忘录模式就是A买了一个礼物,就保存一个备忘录,可能一开始这些礼物,同事都不喜欢,后来同事都其中一个感兴趣了,只要回到当初保存的备忘录
中即可,这个比较简单只是在MementoManager中本来保存的信息改为保存一个map<String,Memento>,将各个需要的备忘都存放进去,用的时候取相应的即可。
这里有个问题就是内存可能溢出,所以保存备忘录时要注意。
参考大牛博客:http://www.cnblogs.com/zuoxiaolong/p/pattern17.html