备忘录模式---给你一瓶后悔药

        声明:文章内容根据大牛博客的内容,自己理解后,给自己做的学习笔记,文末会附上大牛博客地址链接。有需要沟通交流的可加我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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值