美文网首页
设计模式《备忘录模式》

设计模式《备忘录模式》

作者: 天道__ | 来源:发表于2018-08-10 16:43 被阅读0次

    引言

      上一节我们说了享元模式,这节本来打算说访问者模式的,但是太抽象,而且用的不多,所以本章节讲备忘录模式

    示例地址

      Demo

    类图

    image

    定义

      在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样,以后就可以将该对象恢复到原先保存的状态。

    使用场景

      1. 需要保存一个对象在某一个时刻的状态或部分状态。
      2. 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以间接访问其内部状态。

    备忘录模式角色

      Originator:负责创建一个备忘录,可以记录、恢复自身的内部状态。同时Originator还可以根据需要决定Memento存储自身的那些内部状态。
      Memento:备忘录角色,用于存储Originator的内部状态,并且可以防止Originator以外的对象访问Memento。
      Caretaker:负责存储备忘录,不能对备忘录的内容进行操作和访问,只能够将备忘录传递给其他对象。

    备忘录模式中的相关概念

      1. 窄接口:

        只允许它把备忘录对象传给其他的对象。针对的是负责人对象和其他除发起人对象之外的任何对象。

      2. 宽接口:

        允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。针对发起人。

    备忘录模式中的四种模式

    • 白箱模式

      将发起人角色的状态存储在一个大家都看得到的地方,备忘录角色的内部所存储的状态就对所有对象公开。因此这个实现又叫做“白箱实现”。

    1. 定义备忘录
    /**
     * 定义备忘录
     *
     * @author 512573717@qq.com
     * @created 2018/8/9  上午11:23.
     */
    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;
        }
    }
    
    2. 创建发起人角色
    /**
     * 创建发起人角色
     *
     * @author 512573717@qq.com
     * @created 2018/8/9  上午10:59.
     */
    public class Originator {
        private String state;
    
        public String getState() {
            return state;
        }
    
        public void setState(String state) {
            this.state = state;
            System.out.println("当前状态:" + this.state);
        }
    
        /**
         * 工厂方法,返回一个新的备忘录对象
         *
         * @return
         */
        public Memento createMemento() {
            return new Memento(state);
        }
    
        /**
         * 将发起人的状态恢复到备忘录对象所记录的状态
         *
         * @param memento
         */
        public void restoreMemento(Memento memento) {
            this.state = memento.getState();
        }
    
        @Override
        public String toString() {
            return "Originator{" +
                    "state='" + state + '\'' +
                    '}';
        }
    }
    
    3. 定义负责人角色
    /**
     * 定义负责人角色
     *
     * @author 512573717@qq.com
     * @created 2018/8/9  上午11:28.
     */
    public class Caretaker {
        private Memento memento;
    
        public Memento getMemento() {
            return memento;
        }
    
        public void setMemento(Memento memento) {
            this.memento = memento;
        }
    
    }
    
    4. Client
      Originator originator = new Originator();
      originator.setState("state1");
      System.out.println(originator);
    
      Caretaker caretaker = new Caretaker();
      caretaker.setMemento(originator.createMemento());
      originator.setState("state2");
      System.out.println(originator);
    
      originator.restoreMemento(caretaker.getMemento());
      System.out.println(originator);
    
    • 黑箱模式

      备忘录角色对发起人(Originator)角色对象提供一个宽接口,而为其他对象提供一个窄接口。这样的实现叫做“黑箱实现”。 简单说就是备忘录是一个接口,实现类放到发起人里面去了,外界看不见。

    1. 定义接口
    /**
     * 定义备忘录接口
     *
     * @author 512573717@qq.com
     * @created 2018/8/10  上午10:18.
     */
    public interface MementoIF {
    }
    
    
    2. 创建发起人角色
    /**
     *  创建发起人角色
     * 
     * @author 512573717@qq.com
     
     * @created 2018/8/10  下午2:04.
     * 
     */
    public class Originator {
    
        public String state;
    
        public String getState() {
            return state;
        }
    
        public void setState(String state) {
            this.state = state;
            System.out.println("赋值状态:" + state);
        }
    
        /**
         * 工厂方法,返还一个新的备忘录对象
         */
        public Memento createMemento() {
            return new Memento(this);
        }
    
        /**
         * 将发起人恢复到备忘录对象所记录的状态上
         */
        public void restoreMemento(MementoIF memento) {
            this.state = ((Memento) memento).getState();
        }
    
        @Override
        public String toString() {
            return "Originator{" +
                    "state='" + state + '\'' +
                    '}';
        }
    
        private class Memento implements MementoIF {
    
            private String state;
    
            /**
             * 构造方法
             */
            private Memento(Originator o) {
                this.state = o.state;
            }
    
            private String getState() {
                return state;
            }
    
        }
    }
    
    3. 定义负责人角色
    /**
     * 定义负责人角色
     *
     * @author 512573717@qq.com
     * @created 2018/8/10  下午2:04.
     */
    public class Caretaker {
    
        private MementoIF memento;
    
        /**
         * 备忘录取值方法
         */
        public MementoIF retrieveMemento() {
            return memento;
        }
    
        /**
         * 备忘录赋值方法
         */
        public void saveMemento(MementoIF memento) {
            this.memento = memento;
        }
    }
    
    4. Client
        Originator o = new Originator();
        Caretaker c = new Caretaker();
        //改变负责人对象的状态
        o.setState("state1");
        System.out.println("保存中"+o);
        //创建备忘录对象,并将发起人对象的状态存储起来
        c.saveMemento(o.createMemento());
        //修改发起人对象的状态
        o.setState("state2");
        System.out.println("设置新的值"+o);
        //恢复发起人对象的状态
        o.restoreMemento(c.retrieveMemento());
        System.out.println("恢复上次的值"+o);
    
    
    • 多重检查点

      常见的系统往往需要存储不止一个状态,而是需要存储多个状态,或者叫做有多个检查点。用list或者map存储,这种叫做多重检查点

    1. 定义备忘录
    /**
     * 定义备忘录
     *
     * @author 512573717@qq.com
     * @created 2018/8/9  上午11:23.
     */
    public class Memento {
        private List<String> states;
    
        /**
         * 构造函数
         */
        public Memento(List<String> states) {
            //每次存放新的
            this.states = new ArrayList<String>(states);
        }
    
        public List<String> getStates() {
            return states;
        }
    }
    
    2. 创建发起人角色
    /**
     *  创建发起人角色
     *
     * @author 512573717@qq.com
    
     * @created 2018/8/10  下午2:04.
     *
     */
    public class Originator {
        private List<String> states;
        /**
         * 构造函数
         */
        public Originator(){
            states = new ArrayList<String>();
        }
        /**
         * 工厂方法,返还一个新的备忘录对象
         */
        public Memento createMemento(){
            return new Memento(states );
        }
        /**
         * 将发起人恢复到备忘录对象记录的状态上
         */
        public void restoreMemento(Memento memento){
            states = memento.getStates();
        }
        /**
         * 状态的赋值方法
         */
        public void setState(String state){
            states.add(state);
        }
        /**
         * 辅助方法,打印所有状态
         */
        public void printStates(){
    
            for(String state : states){
                System.out.println(state);
            }
        }
    }
    
    3. 定义负责人角色
    /**
     * 定义负责人角色
     *
     * @author 512573717@qq.com
     * @created 2018/8/9  上午11:28.
     */
    public class Caretaker {
    
        private Originator o;
        private List<Memento> mementos = new ArrayList<Memento>();
        /**
         * 构造函数
         */
        public Caretaker(Originator o){
            this.o = o;
        }
        /**
         * 创建一个新的检查点
         */
        public void createMemento(){
            Memento memento = o.createMemento();
            mementos.add(memento);
        }
        /**
         * 将发起人恢复到某个检查点
         */
        public void restoreMemento(int index){
            Memento memento = mementos.get(index);
            o.restoreMemento(memento);
        }
        /**
         * 将某个检查点删除
         */
        public void removeMemento(int index){
            mementos.remove(index);
        }
    }
    
    4. Client
        //多重点检查
        Originator o = new Originator();
        Caretaker c = new Caretaker(o);
        //改变状态
        o.setState("state 0");
        //建立一个检查点
        c.createMemento();
        //改变状态
        o.setState("state 1");
        //建立一个检查点
        c.createMemento();
        //改变状态
        o.setState("state 2");
        //建立一个检查点
        c.createMemento();
        //改变状态
        o.setState("state 3");
        //建立一个检查点
        c.createMemento();
        //改变状态
        o.setState("state 4");
        //建立一个检查点
        c.createMemento();
        //打印出所有检查点
        o.printStates();
        System.out.println("-----------------恢复检查点-----------------");
        //恢复到第二个检查点
        c.restoreMemento(2);
        //打印出所有检查点
        o.printStates();
    
    • 自述历史模式

      发起人角色自己兼任负责人角色。

    1. 定义备忘录接口
    /**
     * 定义备忘录接口
     *
     * @author 512573717@qq.com
     * @created 2018/8/10  上午10:18.
     */
    public interface MementoIF {
    }
    
    2. 创建发起人角色
    /**
     * 创建发起人角色
     *
     * @author 512573717@qq.com
     * @created 2018/8/10  下午2:04.
     */
    public class Originator {
    
        public String state;
    
    
        /**
         * 改变状态
         */
        public void setState(String state) {
            this.state = state;
        }
    
        /**
         * 工厂方法,返还一个新的备忘录对象
         */
        public Memento createMemento() {
            return new Memento(this);
        }
    
        /**
         * 将发起人恢复到备忘录对象所记录的状态上
         */
        public void restoreMemento(MementoIF memento) {
            Memento m = (Memento) memento;
            setState(m.state);
        }
    
        @Override
        public String toString() {
            return "Originator{" +
                    "state='" + state + '\'' +
                    '}';
        }
    
        private class Memento implements MementoIF {
    
            private String state;
    
            /**
             * 构造方法
             */
            private Memento(Originator o) {
                this.state = o.state;
            }
    
            private String getState() {
                return state;
            }
    
        }
    }
    
    3. Client
        Originator o = new Originator();
        //修改状态
        o.setState("state 0");
        //创建备忘录
        MementoIF memento = o.createMemento();
        //修改状态
        o.setState("state 1");
        //按照备忘录恢复对象的状态
        o.restoreMemento(memento);
    

    备忘录模式示例

      最近腾讯有一款很火的游戏,叫做王者荣耀,当我们正在打游戏的时候,突然来电话了,这个时候怎么处理。来看看我们的备忘录模式怎么实现。

    1. 发起人角色(王者荣耀)
    /**
     * 王者荣耀 游戏
     *
     * @author 512573717@qq.com
     * @created 2018/8/9  上午11:53.
     */
    public class WangZheRongYao {
    
        //开始的总时长
        private int time;
    
        // 当前的等级
        private int level;
    
        // 是否退出
        private boolean isExit = false;
    
        /**
         * 开始一把排位
         */
        public void playGame() {
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    while (!isExit) {
                        System.out.println("游戏开始了:" + time + "分钟,等级:" + level);
                        time++;
                        level++;
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    
        /**
         * 来电话了,退出当前游戏
         */
        public void exitGame() {
            isExit = true;
            System.out.println("=====来电话了,退出当前游戏=====");
            System.out.println("游戏开始了:" + time + "分钟,等级:" + level);
        }
    
        /**
         * 保存获取当前游戏信息
         *
         * @return
         */
        public GameInfo saveGameInfo() {
            return new GameInfo(time, level);
        }
    
        /**
         * 重新加载游戏
         *
         * @param gameInfo
         */
        public void resetGame(GameInfo gameInfo) {
            time = gameInfo.getTime();
            level = gameInfo.getLevel();
            System.out.println("=====恢复游戏=====");
            System.out.println("游戏开始了:" + time + "分钟,等级:" + level);
    //        isExit = false;
        }
    }
    
    2. 备忘录
    /**
     * 创建备忘录
     *
     * @author 512573717@qq.com
     * @created 2018/8/9  下午1:57.
     */
    public class GameInfo {
        //开始的总时长
        private int time;
        // 当前的等级
        private int level;
    
        public GameInfo(int time, int level) {
            this.time = time;
            this.level = level;
        }
    
        public int getTime() {
            return time;
        }
    
        public void setTime(int time) {
            this.time = time;
        }
    
        public int getLevel() {
            return level;
        }
    
        public void setLevel(int level) {
            this.level = level;
        }
    }
    
    3. 负责人角色
    /**
     * 备忘录管理者
     *
     * @author 512573717@qq.com
     * @created 2018/8/9  下午1:59.
     */
    public class GameManager {
        private GameInfo mGameInfo;
    
        private static volatile GameManager instance;
    
        private GameManager() {
        }
    
        public static GameManager getGameManager() {
            if (instance == null) {
                synchronized (GameManager.class) {
                    if (instance == null) {
                        instance = new GameManager();
                    }
                }
            }
            return instance;
        }
    
        /**
         * 保存游戏信息
         *
         * @param gameInfo
         */
        public void saveGameInfo(GameInfo gameInfo) {
            mGameInfo = gameInfo;
        }
    
        /**
         * 读取游戏信息
         *
         * @return
         */
        public GameInfo getGameInfo() {
            return mGameInfo;
        }
    }
    
    4. Client
        WangZheRongYao wzry = new WangZheRongYao();
        wzry.playGame();
        try {
            //玩了一会
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //暂停游戏
        wzry.exitGame();
        GameManager.getGameManager().saveGameInfo(wzry.saveGameInfo());
    
        //恢复游戏
        wzry.resetGame(GameManager.getGameManager().getGameInfo());
    

    总结

    优点

       1. 它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
      2. 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。

    缺点

      资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。

    相关文章

      网友评论

          本文标题:设计模式《备忘录模式》

          本文链接:https://www.haomeiwen.com/subject/zmqxbftx.html