精华内容
下载资源
问答
  • 备忘录模式
    2022-05-08 19:11:42

    备忘录模式详解

    备忘录模式又叫作快照模式或Token模式,是一种对象的行为模式。在备忘录模式里,一个备忘录是一个对象,它存储另一个对象(原发器对象)在某个瞬间的内部状态。备忘的目的就是为了以后在需要的时候,可以将原发器对象的状态恢复到备忘录所保存的状态。
    备忘录的本质:保存和恢复状态
    设计意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态了。

    备忘录模式的结构

    备忘录模式涉及的角色及其职责如下:

    原发器角色:原发器根据需要决定将自己的哪些内部状态保存到备忘录中,并可以使用备忘录来恢复内部状态。
    备忘录角色:负责存储原发器对象的内部状态,但是具体需要存储哪些状态是由原发器对象来决定的。另外备忘录应该只能由原发器对象来访问它内部的数据,原发器外部的对象不应该访问到备忘录对象的内部数据。
    为了控制对备忘录对象的访问,备忘录模式中出现了窄接口和宽接口的概念。
    窄接口:管理者只能看到备忘录的窄接口,窄接口的实现中通常没有任何的方法,只是一个类型标识。窄接口使得管理者只能将备忘录传递给其他对象。
    宽接口:原发器能够看到备忘录的宽接口,从而可以从备忘录中获取到所需的数据,来将自己恢复到备忘录中所保存的状态。理想情况是:只允许生成备忘录的原发器来访问该备忘录的内部状态,通常实现成为原发器内的一个私有内部类。
    管理者(Caretaker)角色:备忘录管理者,或者称为备忘录负责人。主要负责保存好备忘录对象,但是不能对备忘录对象的内容进行操作或检查。

    示例:

    
    /**
     * <h3>design-mode</h3>
     * <p>备忘录的窄接口,没有任何方法定义</p>
     *
     * @author : ZhangYuJie
     * @date : 2022-05-08 19:16
     **/
    
    

    再看看原发器角色,它里面会有备忘录对象的实现,此处将真正的备忘录对象当作原发器对象的一个私有内部类来实现。示例代码:

    package com.example.designmode.memento;
    
    /**
     * <h3>design-mode</h3>
     * <p></p>
     *
     * @author : ZhangYuJie
     * @date : 2022-05-08 19:16
     **/
    
    public class Originator {
        /**
         * 示意,表示原发器的状态
         */
        private String state = "";
    
        /**
         * 创建备忘录,保存原发器的状态
         *
         * @return 创建好的备忘录对象
         */
        public Memento createMemento() {
            return new MementoImpl(state);
        }
    
        /**
         * 将原发器恢复到备忘录中保存的状态
         * 保存有原发器状态的备忘录对象
         * @param
         */
        public void recoverFromMemento(Memento memento) {
            MementoImpl mementoImpl = (MementoImpl) memento;
            this.state = mementoImpl.getState();
        }
    
        public String getState() {
            return state;
        }
    
        public void setState(String state) {
            this.state = state;
        }
    
        /**
         * 真正的备忘录对象,实现了备忘录窄接口 实现成私有的内部类,不让外部访问
         */
        private static class MementoImpl implements  Memento {
    
            /**
             * 示意,表示需要保存的状态
             */
            private String state = "";
    
            public MementoImpl(String state) {
                super();
                this.state = state;
            }
    
            public String getState() {
                return state;
            }
        }
    
    }
    
    

    备忘录管理者的示例代码。

    package com.example.designmode.memento;
    
    /**
     * <h3>design-mode</h3>
     * <p></p>
     *
     * @author : ZhangYuJie
     * @date : 2022-05-08 19:16
     **/
    
    public class Caretaker {
    
        /**
         * 记录被保存的备忘录对象
         */
        private Memento memento = null;
    
        public Memento getMemento() {
            return memento;
        }
    
        public void setMemento(Memento memento) {
            this.memento = memento;
        }
    
    }
    
    

    main方法来测试一下:

    package com.example.designmode.memento;
    
    /**
     * <h3>design-mode</h3>
     * <p></p>
     *
     * @author : ZhangYuJie
     * @date : 2022-05-08 19:18
     **/
    
    public class Client {
        public static void main(String[] args) {
            // 创建一个原发器
            Originator o = new Originator();
            // 设置其初始状态
            o.setState("state 0");
            // 打印原发器当前的状态
            System.out.println("原发器的初始状态:" + o.getState());
            // 将原发器当前的状态保存到备忘录中
            Memento memento = o.createMemento();
            // 创建一个管理者
            Caretaker c = new Caretaker();
            // 将创建好的备忘录交给管理者进行管理
            c.setMemento(memento);
            // 改变原发器的状态
            o.setState("state 1");
            // 打印原发器当前的状态
            System.out.println("原发器改變后状态:" + o.getState());
            // 将原发器状态恢复到备忘录保存的状态
            o.recoverFromMemento(c.getMemento());
            // 再次打印原发器当前的状态
            System.out.println("原发器恢復后状态:" + o.getState());
        }
    }
    
    

    结果:

    原发器的初始状态:state 0
    原发器改變后状态:state 1
    原发器恢復后状态:state 0
    

    备忘录模式适用场景:

    如果必须保存一个对象在某一个时刻的状态,方便在以后需要的时候,可以把该对象恢复到先前的状态

    备忘录模式的优缺点:

    优点
    在备忘录模式中,原发器不再需要管理和保存其内部状态的一个个版本,而是交由管理者或客户端对这些状态的版本进行管理,从而让原发器对象得到简化
    缺点:
    管理者负责维护备忘录,但是管理者并不知道备忘录中有多少个状态。因此当存储备忘录时,一个本来很小的管理者,可能会产生大量的存储消耗

    更多相关内容
  • 本文实例讲述了Python设计模式之备忘录模式原理与用法。分享给大家供大家参考,具体如下: 备忘录模式(Memento Pattern):不破坏封装性的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,这样已经后就可将...
  • 本文实例讲述了javascript设计模式 – 备忘录模式原理与用法。分享给大家供大家参考,具体如下: 介绍:在我们的开发中偶尔会遇到这样一种情况,需要对用户的行为进行撤销。要想实现撤销,首先需要保存软件系统的...
  • Memento备忘录模式 备忘录模式一个最好想象的例子:undo! 它对对象的一个状态进行了’快照’, 在你需要的时候恢复原貌。做前端会有一个场景:你设计一个表单,当点击提交会对表单内容 验证,这个时候你就要对用户...
  • 何为备忘录模式?  在响应某些事件时,应用程序需要保存自身的状态,比如当用户保存文档或程序退出时。例如,游戏退出之前,可能需要保存当前会话的状态,如游戏等级、敌人数量、可用武器的种类等。游戏再次打开时...
  • c# 备忘录模式

    2020-09-05 18:15:05
    备忘录模式:在不破坏封装的前提下,捕获一个对象的内部状态,并在这个对象之外的地方保存这个状态,这样以后就可将该对象恢复到原来保存的状态了
  • 主要介绍了C++设计模式之备忘录模式,本文讲解了什么是备忘录模式备忘录模式的UML类图、备忘录模式的使用场合等内容,需要的朋友可以参考下
  • 备忘录模式

    2017-11-04 15:50:51
    23种设计模式之备忘录模式在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样就可以在以后将对象恢复到原先保存的状态。
  • 备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 备忘录模式java demo
  • 主要介绍了JAVA设计模式之备忘录模式的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下
  • 主要介绍了php设计模式中的备忘录模式,使用php实现备忘录模式,感兴趣的小伙伴们可以参考一下
  • 主要介绍了设计模式中的备忘录模式解析及相关C++实例应用,备忘录模式也经常被用来在命令模式中维护可以撤销(Undo)操作的状态,需要的朋友可以参考下
  • 主要介绍了详解备忘录模式及其在Java设计模式编程中的实现,备忘录模式数据的存储过程中应当注意浅拷贝和深拷贝的问题,需要的朋友可以参考下
  • 主要介绍了Android编程设计模式之备忘录模式,结合实例形式详细分析了Android备忘录模式的概念、原理、应用场景、用法及相关操作注意事项,需要的朋友可以参考下
  • 备忘录模式(Memento Design Pattern),也叫快照(Snapshot)模式。指在不违背封装原则前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。 备忘录模式在日常中很常见,...

    备忘录模式(Memento Design Pattern),也叫快照(Snapshot)模式。指在不违背封装原则前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。

    备忘录模式在日常中很常见,比如Word中的回退,MySQL中的undo log日志,Git版本管理等等,我们都可以从当前状态退回之前保存的状态。比如Git中的checkout命令就可以从main版本切换到之前的bugFix版本:

    image-20220111160556720

    一、备忘录模式介绍

    备忘录是一种对象行为型模式,它提供了一种可以恢复状态的机制,并实现了内部状态的封装。下面就来看看备忘录模式的结构及其对应的实现:

    1.1 备忘录模式的结构

    备忘录的核心是备忘录类(Memento)和管理备忘录的管理者类(Caretaker)的设计,其结构如下图所示:

    image-20220408211806936

    • Originator:组织者类,记录当前业务的状态信息,提供备忘录创建和恢复的功能
    • Memento:备忘录类,存储组织者类的内部状态,在需要时候提供这些内部状态给组织者类
    • Caretaker:管理者类,对备忘录进行管理,提供存储于获取备忘录的功能,无法对备忘录对象进行修改和访问

    1.2 备忘录模式的实现

    在利用备忘录模式时,首先应该设计一个组织者类(Originator),它是一个具体的业务类,存储当前状态。它包含备忘录对象的创建方法createMemeto()和备忘录对象恢复方法restoreMemeto()

    Originator类的具体代码如下:

    public class Originator {
        private String state;
    
        public String getState() {
            return state;
        }
    
        public void setState(String state) {
            this.state = state;
        }
    	
        //创建一个备忘录对象
        public Memento createMemento() {
            return new Memento(this);
        }
    	
        //根据备忘录对象,恢复之前组织者的状态
        public void restoreMemento(Memento m) {
            state = m.getState();
        }
    }
    

    对于备忘录类(Memento)而言,它存储组织者类(Originator)的状态,其具体代码如下:

    public class Memento {
    
        private String state;
    
        public Memento(Originator o) {
            this.state = state;
        }
    
        public String getState() {
            return state;
        }
    
        public void setState(String state) {
            this.state = state;
        }
    }
    

    在这里需要考虑备忘录的封装性,除了Originator类外,其他类不能调用备忘录的内部的相关方法。因为外界类的调用可能会引起备忘录内的状态发生变化,这样备忘录的设置就没有了意义。在实际操作中,可以将MementoOriginator类定义在同一个包中来实现封装;也可以将Memento类作为Originator的内部类。

    下面再了看看管理者类(Caretaker)的具体代码:

    public class Caretaker {
    
        private Memento memento;
    
    
        public Memento getMemento() {
            return memento;
        }
    
        public void setMemento(Memento memento) {
            this.memento = memento;
        }
    }
    

    它的作用仅仅是存储备忘录对象,而且其内部中也不应该有直接调用Memento中的状态改变方法。只有当用户需要对Originator类进行恢复时,再将存储在其中的备忘录对象取出。

    下面是对整个流程的测试:

    public class Client {
        public static void main(String[] args) {
            Originator originator = new Originator();
            Caretaker caretaker = new Caretaker();
            //在originator和caretaker中保存memento对象
            originator.setState("1");
            System.out.println("当前的状态是:" + originator.getState());
            caretaker.setMemento(originator.createMemento());
    
            originator.setState("2");
            System.out.println("当前的状态是:" + originator.getState());
            //从Caretaker取出Memento对象
            originator.restoreMemento(caretaker.getMemento());
            System.out.println("执行状态恢复,当前的状态是:" + originator.getState());
    
        }
    }
    

    测试结果为:

    当前的状态是:1
    当前的状态是:2
    执行状态恢复,当前的状态是:1
    

    二、备忘录模式的应用场景

    正如开头提到的,备忘录模式可以用在诸如Word文字编辑器,PhotoShop等软件的状态保存,还有数据库的备份等等场景。下面引用一个文本编辑的代码实现,来自于《设计模式》

    2.1 实现文本编辑器恢复功能

    /**
     * @description: 输入text的当前状态
     * @author: wjw
     * @date: 2022/4/8
     */
    public class InputText {
        
        private StringBuilder text = new StringBuilder();
    
        public StringBuilder getText() {
            return text;
        }
    
        public void setText(StringBuilder text) {
            this.text = text;
        }
        
        //创建SnapMemento对象
        public SnapMemento createSnapMemento() {
            return new SnapMemento(this);
        }
        
        //恢复SnapMemento对象
        public void restoreSnapMemento(SnapMemento sm) {
            text = sm.getText(); 
        }
    }
    /**
     * @description: 快照备忘录
     * @author: wjw
     * @date: 2022/4/8
     */
    public class SnapMemento {
    
        private StringBuilder text;
    
        public SnapMemento(InputText it) {
            text = it.getText();
        }
    
        public StringBuilder getText() {
            return text;
        }
    
        public void setText(StringBuilder text) {
            this.text = text;
        }
    }
    /**
     * @description: 负责SnapMemento对象的获取和存储
     * @author: wjw
     * @date: 2022/4/8
     */
    public class SnapMementoHolder {
        private Stack<SnapMemento> snapMementos = new Stack<>();
    
        //获取snapMemento对象
        public SnapMemento popSnapMemento() {
            return snapMementos.pop();
        }
    
        //存储snapMemento对象
        public void pushSnapMemento(SnapMemento sm) {
            snapMementos.push(sm);
        }
    }
    /**
     * @description: 客户端
     * @author: wjw
     * @date: 2022/4/8
     */
    public class test_memento {
        public static void main(String[] args) {
            InputText inputText = new InputText();
            StringBuilder first_stringBuilder = new StringBuilder("First StringBuilder");
            inputText.setText(first_stringBuilder);
            SnapMementoHolder snapMementoHolder = new SnapMementoHolder();
            snapMementoHolder.pushSnapMemento(inputText.createSnapMemento());
    
            System.out.println("当前的状态是:" + inputText.getText().toString());
            StringBuilder second_stringBuilder = new StringBuilder("Second StringBuilder");
            inputText.setText(second_stringBuilder);
            System.out.println("修改过后的状态是:" + inputText.getText().toString());
            inputText.restoreSnapMemento(snapMementoHolder.popSnapMemento());
            System.out.println("利用备忘录恢复的状态:" + inputText.getText().toString());
    
        }
    }
    

    测试结果:

    当前的状态是:First StringBuilder
    修改过后的状态是:Second StringBuilder
    利用备忘录恢复的状态:First StringBuilder
    

    三、备忘录模式实战

    在本案例中模拟系统在发布上线的过程中记录线上配置文件用于紧急回滚(案例来源于《重学Java设计模式》):

    image-20220408215739272

    其中配置文件中包含版本、时间、MD5、内容信息和操作人。如果一旦遇到紧急问题,系统可以通过回滚操作将配置文件回退到上一个版本中。那么备忘录存储的信息就是配置文件的内容,根据备忘录模式设计该结构:

    image-20220408221452228

    • ConfigMemento:备忘录类,是对原有配置类的扩展
    • ConfigOriginator:记录者类,相当于之前的管理者(Caretaker),获取和返回备忘录对象
    • Admin:管理员类,操作修改备忘信息,相当于之前的组织者(Originator)

    具体代码实现

    1. ConfigFile配置信息类
    public class ConfigFile {
    
        private String versionNo;
        private String content;
        private Date dateTime;
        private String operator;
        
        //getset,constructor
    }
    
    1. ConfigMemento备忘录类
    public class ConfigMemento {
    
        private ConfigFile configFile;
    
        public ConfigMemento(ConfigFile configFile) {
            this.configFile = configFile;
        }
    
        public ConfigFile getConfigFile() {
            return configFile;
        }
    
        public void setConfigFile(ConfigFile configFile) {
            this.configFile = configFile;
        }
    }
    
    1. ConfigOriginator配置文件组织者类
    public class ConfigOriginator {
    
        private ConfigFile configFile;
    
        public ConfigFile getConfigFile() {
            return configFile;
        }
    
        public void setConfigFile(ConfigFile configFile) {
            this.configFile = configFile;
        }
    
        public ConfigMemento saveMemento() {
            return new ConfigMemento(configFile);
        }
    
        public void getMemento(ConfigMemento memento) {
            this.configFile = memento.getConfigFile();
        }
    }
    
    1. Admin配置文件管理者类
    public class Admin {
    
        //版本信息
        private int cursorIdx = 0;
        private List<ConfigMemento> mementoList = new ArrayList<>();
        private Map<String, ConfigMemento> mementoMap = new ConcurrentHashMap<String, ConfigMemento>();
    
        //新增版本信息
        public void append(ConfigMemento memento) {
            mementoList.add(memento);
            mementoMap.put(memento.getConfigFile().getVersionNo(), memento);
            cursorIdx++;
        }
    
        //回滚历史配置
        public ConfigMemento undo() {
            if (--cursorIdx <= 0) {
                return mementoList.get(0);
            }
            return mementoList.get(cursorIdx);
        }
    
        //前进历史配置
        public ConfigMemento redo() {
            if(++cursorIdx > mementoList.size()) {
                return mementoList.get(mementoList.size() - 1);
            }
            return mementoList.get(cursorIdx);
        }
    
        public ConfigMemento get(String versionNo) {
            return mementoMap.get(versionNo);
        }
    
    }
    
    1. 测试类及结果
    public class ApiTest {
    
        private Logger logger = LoggerFactory.getLogger(ApiTest.class);
    
        @Test
        public void test_memento() {
            Admin admin = new Admin();
            ConfigOriginator configOriginator = new ConfigOriginator();
    
            configOriginator.setConfigFile(new ConfigFile("1000001", "配置内容1", new Date(), "ethan"));
            admin.append(configOriginator.saveMemento());
    
            configOriginator.setConfigFile(new ConfigFile("1000002", "配置内容2", new Date(), "ethan"));
            admin.append(configOriginator.saveMemento());
    
            configOriginator.setConfigFile(new ConfigFile("1000003", "配置内容3", new Date(), "ethan"));
            admin.append(configOriginator.saveMemento());
    
            configOriginator.setConfigFile(new ConfigFile("1000004", "配置内容4", new Date(), "ethan"));
            admin.append(configOriginator.saveMemento());
    
            //(第一次回滚)
            configOriginator.getMemento(admin.undo());
            logger.info("回滚undo: {}", JSON.toJSONString(configOriginator.getConfigFile()));
    
            //(第二次回滚)
            configOriginator.getMemento(admin.undo());
            logger.info("回滚undo: {}", JSON.toJSONString(configOriginator.getConfigFile()));
    
            // (前进)
            configOriginator.getMemento(admin.redo());
            logger.info("前进redo:{}", JSON.toJSONString(configOriginator.getConfigFile()));
    
            // (获取)
            configOriginator.getMemento(admin.get("1000002"));
            logger.info("获取get:{}", JSON.toJSONString(configOriginator.getConfigFile()));
    
        }
    }
    

    测试结果:

    22:44:39.773 [main] INFO  ApiTest - 回滚undo: {"content":"配置内容4","dateTime":1649429079642,"operator":"ethan","versionNo":"1000004"}
    22:44:39.777 [main] INFO  ApiTest - 回滚undo: {"content":"配置内容3","dateTime":1649429079642,"operator":"ethan","versionNo":"1000003"}
    22:44:39.777 [main] INFO  ApiTest - 前进redo:{"content":"配置内容4","dateTime":1649429079642,"operator":"ethan","versionNo":"1000004"}
    22:44:39.777 [main] INFO  ApiTest - 获取get:{"content":"配置内容2","dateTime":1649429079642,"operator":"ethan","versionNo":"1000002"}
    

    参考资料

    《Java设计模式》

    《设计模式》

    《重学Java设计模式》

    http://c.biancheng.net/view/1400.html

    展开全文
  • 战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态 传统方案解决游戏角色恢复 传统的设计方案(类图) 传统的方式的问题分析 1) 一个对象,就对应一个保存对象状态的对象, 这样当我们游戏的对象很...
    游戏角色状态恢复问题
            游戏角色有攻击力和防御力,在大战Boss 前保存自身的状态 ( 攻击力和防御力 ) ,当大
    Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态

    传统方案解决游戏角色恢复
            传统的设计方案(类图)
     
    传统的方式的问题分析
            1) 一个对象,就对应一个保存对象状态的对象, 这样当我们游戏的对象很多时,不
    利于管理,开销也很大 .
            2) 传统的方式是简单地做备份, new 出另外一个对象出来,再把需要备份的数据放到
    这个新对象,但这就暴露了对象内部的细节
            3) 解决方案: => 备忘录模式
     
    备忘录模式基本介绍
            1) 备忘录模式( Memento Pattern )在不破坏封装性的前提下,捕获一个对象的内
    部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保
    存的状态
            2) 可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,
    或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录
    模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某
    些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作
            3) 备忘录模式属于行为型模式
    备忘录模式的原理类图
     
    对原理类图的说明-即 (备忘录模式的角色及职责)

             1) originator : 对象(需要保存状态的对象)
             2) Memento : 备忘录对象,负责保存好记录,即Originator内部状态
             3) Caretaker: 守护者对象,负责保存多个备忘录对象, 使用集合管理,提高效率
             4) 说明:如果希望保存多个originator对象的不同时间的状态,也可以,只需要
    要 HashMap <String, 集合>

    代码:

    public class Originator {
    
    	private String state;//状态信息
    
    	public String getState() {
    		return state;
    	}
    
    	public void setState(String state) {
    		this.state = state;
    	}
    	
    	//编写一个方法,可以保存一个状态对象 Memento
    	//因此编写一个方法,返回 Memento
    	public Memento saveStateMemento() {
    		return new Memento(state);
    	}
    	
    	//通过备忘录对象,恢复状态
    	public void getStateFromMemento(Memento memento) {
    		state = memento.getState();
    	}
    }
    public class Memento {
    	private String state;
    
    	//构造器
    	public Memento(String state) {
    		super();
    		this.state = state;
    	}
    
    	public String getState() {
    		return state;
    	}
    	
    	
    	
    }
    import java.util.ArrayList;
    import java.util.List;
    
    public class Caretaker {
    	
    	//在List 集合中会有很多的备忘录对象
    	private List<Memento> mementoList = new ArrayList<Memento>();
    	
    	public void add(Memento memento) {
    		mementoList.add(memento);
    	}
    	
    	//获取到第index个Originator 的 备忘录对象(即保存状态)
    	public Memento get(int index) {
    		return mementoList.get(index);
    	}
    }
    import java.util.ArrayList;
    import java.util.HashMap;
    
    public class Client {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		
    		Originator originator = new Originator();
    		Caretaker caretaker = new Caretaker();
    		
    		originator.setState(" 状态#1 攻击力 100 ");
    		
    		//保存了当前的状态
    		caretaker.add(originator.saveStateMemento());
    		
    		originator.setState(" 状态#2 攻击力 80 ");
    		
    		caretaker.add(originator.saveStateMemento());
    		
    		originator.setState(" 状态#3 攻击力 50 ");
    		caretaker.add(originator.saveStateMemento());
    		
    		
    		
    		System.out.println("当前的状态是 =" + originator.getState());
    		
    		//希望得到状态 1, 将 originator 恢复到状态1
    		
    		originator.getStateFromMemento(caretaker.get(0));
    		System.out.println("恢复到状态1 , 当前的状态是");
    		System.out.println("当前的状态是 =" + originator.getState());
    		
    		
    		
    	}
    
    }

    备忘录模式原理代码实现

            针对前面的备忘录模式原理结构图,我们使用代码来说明,注意体会体现出
    Caretaker 可以保存多个备忘录对象, 方便管理,提高效率。

    代码:
    public class GameRole {
    
    	private int vit;
    	private int def;
    	
    	//创建Memento ,即根据当前的状态得到Memento
    	public Memento createMemento() {
    		return new Memento(vit, def);
    	}
    	
    	//从备忘录对象,恢复GameRole的状态
    	public void recoverGameRoleFromMemento(Memento memento) {
    		this.vit = memento.getVit();
    		this.def = memento.getDef();
    	}
    	
    	//显示当前游戏角色的状态
    	public void display() {
    		System.out.println("游戏角色当前的攻击力:" + this.vit + " 防御力: " + this.def);
    	}
    
    	public int getVit() {
    		return vit;
    	}
    
    	public void setVit(int vit) {
    		this.vit = vit;
    	}
    
    	public int getDef() {
    		return def;
    	}
    
    	public void setDef(int def) {
    		this.def = def;
    	}
    	
    	
    }
    public class Memento {
    
    	//攻击力
    	private int vit;
    	//防御力
    	private int def;
    	public Memento(int vit, int def) {
    		super();
    		this.vit = vit;
    		this.def = def;
    	}
    	public int getVit() {
    		return vit;
    	}
    	public void setVit(int vit) {
    		this.vit = vit;
    	}
    	public int getDef() {
    		return def;
    	}
    	public void setDef(int def) {
    		this.def = def;
    	}
    	
    	
    	
    	
    }
    import java.util.ArrayList;
    import java.util.HashMap;
    
    //守护者对象, 保存游戏角色的状态
    public class Caretaker {
    
    	//如果只保存一次状态
    	private Memento  memento;
    	//对GameRole 保存多次状态
    	//private ArrayList<Memento> mementos;
    	//对多个游戏角色保存多个状态
    	//private HashMap<String, ArrayList<Memento>> rolesMementos;
    
    	public Memento getMemento() {
    		return memento;
    	}
    
    	public void setMemento(Memento memento) {
    		this.memento = memento;
    	}
    	
    	
    }
    public class Client {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		//创建游戏角色
    		GameRole gameRole = new GameRole();
    		gameRole.setVit(100);
    		gameRole.setDef(100);
    		
    		System.out.println("和boss大战前的状态");
    		gameRole.display();
    		
    		//把当前状态保存caretaker
    		Caretaker caretaker = new Caretaker();
    		caretaker.setMemento(gameRole.createMemento());
    		
    		System.out.println("和boss大战~~~");
    		gameRole.setDef(30);
    		gameRole.setVit(30);
    		
    		gameRole.display();
    		
    		System.out.println("大战后,使用备忘录对象恢复到站前");
    		
    		gameRole.recoverGameRoleFromMemento(caretaker.getMemento());
    		System.out.println("恢复后的状态");
    		gameRole.display();
    	}
    
    }
    备忘录模式的注意事项和细节
            1) 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史
    的状态
            2) 实现了信息的封装,使得用户不需要关心状态的保存细节
            3) 如果类的成员变量过多,势必会占用比 较大的资源 ,而且每一次保存都会消耗一定
    的内存 , 这个需要注意
            4) 适用的应用场景: 1 、后悔药。 2 、打游戏时的存档。 3 Windows 里的 ctri + z
                    4、IE 中的后退。 5 、数据库的事务管理
            5) 为了节约内存,备忘录模式可以和原型模式配合使用
    展开全文
  • 主要介绍了设计模式中的Memento备忘录模式的在iOS App开发中的运用,Memento着重于捕获和具体化当前对象的内部状态,需要的朋友可以参考下
  • 在阎宏博士的《JAVA与模式》一书中开头是这样描述备忘录(Memento)模式的:备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式。备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。...

    在阎宏博士的《JAVA与模式》一书中开头是这样描述备忘录(Memento)模式的:

    备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式。

    备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉(Capture)住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。备忘录模式常常与命令模式和迭代子模式一同使用。

    备忘录模式的结构

    备忘录模式的结构图如下所示

    备忘录模式所涉及的角色有三个:备忘录(Memento)角色、发起人(Originator)角色、负责人(Caretaker)角色。

    备忘录(Memento)角色

    备忘录角色又如下责任:

    (1)将发起人(Originator)对象的内战状态存储起来。备忘录可以根据发起人对象的判断来决定存储多少发起人(Originator)对象的内部状态。

    (2)备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。

    备忘录有两个等效的接口:

    ●  窄接口:负责人(Caretaker)对象(和其他除发起人对象之外的任何对象)看到的是备忘录的窄接口(narrow interface),这个窄接口只允许它把备忘录对象传给其他的对象。

    ●  宽接口:与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口(wide interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。

    发起人(Originator)角色

    发起人角色有如下责任:

    (1)创建一个含有当前的内部状态的备忘录对象。

    (2)使用备忘录对象存储其内部状态。

    负责人(Caretaker)角色

    负责人角色有如下责任:

    (1)负责保存备忘录对象。

    (2)不检查备忘录对象的内容。

    “白箱”备忘录模式的实现

    备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开。因此这个实现又叫做“白箱实现”。

    “白箱”实现将发起人角色的状态存储在一个大家都看得到的地方,因此是破坏封装性的。但是通过程序员自律,同样可以在一定程度上实现模式的大部分用意。因此白箱实现仍然是有意义的。

    下面给出一个示意性的“白箱实现”。

    源代码

    发起人角色类,发起人角色利用一个新创建的备忘录对象将自己的内部状态存储起来。

    copycode.gifpublic class Originator {    private String state;    /**

    * 工厂方法,返回一个新的备忘录对象     */

    public Memento createMemento(){        return new Memento(state);

    }    /**

    * 将发起人恢复到备忘录对象所记载的状态     */

    public void restoreMemento(Memento memento){        this.state = memento.getState();

    }

    public String getState() {        return state;

    }

    public void setState(String state) {        this.state = state;

    System.out.println("当前状态:" + this.state);

    }

    }

    copycode.gif

    备忘录角色类,备忘录对象将发起人对象传入的状态存储起来。

    copycode.gifpublic 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;

    }

    }

    copycode.gif

    负责人角色类,负责人角色负责保存备忘录对象,但是从不修改(甚至不查看)备忘录对象的内容。

    copycode.gifpublic class Caretaker {    private Memento memento;    /**

    * 备忘录的取值方法     */

    public Memento retrieveMemento(){        return this.memento;

    }    /**

    * 备忘录的赋值方法     */

    public void saveMemento(Memento memento){        this.memento = memento;

    }

    }

    copycode.gif

    客户端角色类

    copycode.gifpublic class Client {    public static void main(String[] args) {

    Originator o = new Originator();

    Caretaker c = new Caretaker();        //改变负责人对象的状态

    o.setState("On");        //创建备忘录对象,并将发起人对象的状态储存起来        c.saveMemento(o.createMemento());        //修改发起人的状态

    o.setState("Off");        //恢复发起人对象的状态        o.restoreMemento(c.retrieveMemento());

    System.out.println(o.getState());

    }

    }

    copycode.gif

    在上面的这个示意性的客户端角色里面,首先将发起人对象的状态设置成“On”,并创建一个备忘录对象将这个状态存储起来;然后将发起人对象的状态改成“Off”;最后又将发起人对象恢复到备忘录对象所存储起来的状态,即“On”状态。

    系统的时序图更能够反映出系统各个角色被调用的时间顺序。如下图是将发起人对象的状态存储到白箱备忘录对象中去的时序图。

    可以看出系统运行的时序是这样的:

    (1)将发起人对象的状态设置成“On”。

    (2)调用发起人角色的createMemento()方法,创建一个备忘录对象将这个状态存储起来。

    (3)将备忘录对象存储到负责人对象中去。

    将发起人对象恢复到备忘录对象所记录的状态的时序图如下所示:

    可以看出,将发起人对象恢复到备忘录对象所记录的状态时,系统的运行时序是这样的:

    (1)将发起人状态设置成“Off”。

    (2)将备忘录对象从负责人对象中取出。

    (3)将发起人对象恢复到备忘录对象所存储起来的状态,即“On”状态。

    “黑箱”备忘录模式的实现

    备忘录角色对发起人(Originator)角色对象提供一个宽接口,而为其他对象提供一个窄接口。这样的实现叫做“黑箱实现”。

    在JAVA语言中,实现双重接口的办法就是将备忘录角色类设计成发起人角色类的内部成员类。

    将Memento设成Originator类的内部类,从而将Memento对象封装在Originator里面;在外部提供一个标识接口MementoIF给Caretaker以及其他对象。这样,Originator类看到的是Menmento的所有接口,而Caretaker以及其他对象看到的仅仅是标识接口MementoIF所暴露出来的接口。

    使用内部类实现备忘录模式的类图如下所示。

    源代码

    发起人角色类Originator中定义了一个内部的Memento类。由于此Memento类的全部接口都是私有的,因此只有它自己和发起人类可以调用。

    copycode.gifpackage memento.sample2;/**

    * @author chen_dz

    * @date :2012-6-2 上午10:11:08 */public class Originator {    private String state;

    public String getState() {        return state;

    }    public void setState(String state) {        this.state = state;

    System.out.println("赋值状态:" + state);

    }    /**

    * 工厂方法,返还一个新的备忘录对象     */

    public MementoIF createMemento(){        return new Memento(state);

    }    /**

    * 发起人恢复到备忘录对象记录的状态     */

    public void restoreMemento(MementoIF memento){        this.setState(((Memento)memento).getState());

    }

    private class Memento implements MementoIF{

    private String state;        /**

    * 构造方法         */

    private Memento(String state){            this.state = state;

    }

    private String getState() {            return state;

    }        private void setState(String state) {            this.state = state;

    }

    }

    }

    copycode.gif

    窄接口MementoIF,这是一个标识接口,因此它没有定义出任何的方法。

    public interface MementoIF {

    }

    负责人角色类Caretaker能够得到的备忘录对象是以MementoIF为接口的,由于这个接口仅仅是一个标识接口,因此负责人角色不可能改变这个备忘录对象的内容。

    copycode.gifpublic class Caretaker {    private MementoIF memento;    /**

    * 备忘录取值方法     */

    public MementoIF retrieveMemento(){        return memento;

    }    /**

    * 备忘录赋值方法     */

    public void saveMemento(MementoIF memento){        this.memento = memento;

    }

    }

    copycode.gif

    客户端角色类

    copycode.gifpublic class Client {    public static void main(String[] args) {

    Originator o = new Originator();

    Caretaker c = new Caretaker();        //改变负责人对象的状态

    o.setState("On");        //创建备忘录对象,并将发起人对象的状态存储起来        c.saveMemento(o.createMemento());        //修改发起人对象的状态

    o.setState("Off");        //恢复发起人对象的状态        o.restoreMemento(c.retrieveMemento());

    }

    }

    copycode.gif

    客户端首先

    (1)将发起人对象的状态设置为“On”。

    (2)调用createMemento()方法,创建一个备忘录对象将这个状态存储起来(此时createMemento()方法还回的明显类型是MementoIF接口,真实类型为Originator内部的Memento对象)。

    (3)将备忘录对象存储到负责人对象中去。由于负责人对象拿到的仅是MementoIF接口,因此无法读出备忘录对象内部的状态。

    (4)将发起人对象的状态设置为“Off”。

    (5)调用负责人对象的retrieveMemento()方法将备忘录对象取出。注意此时仅能得到MementoIF接口,因此无法读出此对象的内部状态。

    (6)调用发起人对象的restoreMemento()方法将发起人对象的状态恢复成备忘录对象所存储的起来的状态,即“On”状态。由于发起人对象的内部类Memento实现了MementoIF接口,这个内部类是传入的备忘录对象的真实类型,因此发起人对象可以利用内部类Memento的私有接口读出此对象的内部状态。

    多重检查点

    前面所给出的白箱和黑箱的示意性实现都是只存储一个状态的简单实现,也可以叫做只有一个检查点。常见的系统往往需要存储不止一个状态,而是需要存储多个状态,或者叫做有多个检查点。

    备忘录模式可以将发起人对象的状态存储到备忘录对象里面,备忘录模式可以将发起人对象恢复到备忘录对象所存储的某一个检查点上。下面给出一个示意性的、有多重检查点的备忘录模式的实现。

    源代码

    发起人角色源代码

    copycode.gifpublic class Originator {    private List states;    //检查点指数

    private int index;    /**

    * 构造函数     */

    public Originator(){

    states = new ArrayList();

    index = 0;

    }    /**

    * 工厂方法,返还一个新的备忘录对象     */

    public Memento createMemento(){        return new Memento(states , index);

    }    /**

    * 将发起人恢复到备忘录对象记录的状态上     */

    public void restoreMemento(Memento memento){

    states = memento.getStates();

    index = memento.getIndex();

    }    /**

    * 状态的赋值方法     */

    public void setState(String state){

    states.add(state);

    index++;

    }    /**

    * 辅助方法,打印所有状态     */

    public void printStates(){

    for(String state : states){

    System.out.println(state);

    }

    }

    }

    copycode.gif

    备忘录角色类,这个实现可以存储任意多的状态,外界可以使用检查点指数index来取出检查点上的状态。

    copycode.gifpublic class Memento {    private List states;    private int index;    /**

    * 构造函数     */

    public Memento(List states , int index){        this.states = new ArrayList(states);        this.index = index;

    }    public List getStates() {        return states;

    }    public int getIndex() {        return index;

    }

    }

    copycode.gif

    负责人角色类

    copycode.gifpublic class Caretaker {    private Originator o;    private List mementos = new ArrayList();    private int current;    /**

    * 构造函数     */

    public Caretaker(Originator o){        this.o = o;

    current = 0;

    }    /**

    * 创建一个新的检查点     */

    public int createMemento(){

    Memento memento = o.createMemento();

    mementos.add(memento);        return current++;

    }    /**

    * 将发起人恢复到某个检查点     */

    public void restoreMemento(int index){

    Memento memento = mementos.get(index);

    o.restoreMemento(memento);

    }    /**

    * 将某个检查点删除     */

    public void removeMemento(int index){

    mementos.remove(index);

    }

    }

    copycode.gif

    客户端角色源代码

    copycode.gifpublic class Client {    public static void main(String[] args) {

    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.printStates();

    System.out.println("-----------------恢复检查点-----------------");        //恢复到第二个检查点

    c.restoreMemento(2);        //打印出所有检查点        o.printStates();

    }

    }

    copycode.gif

    运行结果如下:

    可以看出,客户端角色通过不断改变发起人角色的状态,并将之存储在备忘录里面。通过指明检查点指数可以将发起人角色恢复到相应的检查点所对应的状态上。

    将发起人的状态存储到备忘录对象中的活动序列图如下:

    系统运行的时序是这样的:

    (1)将发起人对象的状态设置成某个有效状态;

    (2)调用负责人角色的createMemento()方法,负责人角色会负责调用发起人角色和备忘录角色,将发起人对象的状态存储起来。

    将发起人对象恢复到某一个备忘录对象的检查点的活动序列图如下:

    由于负责人角色的功能被增强了,因此将发起人对象恢复到备忘录对象所记录的状态时,系统运行的时序被简化了:

    (1)调用负责人角色的restoreMemento()方法,将发起人恢复到某个检查点。

    “自述历史”模式

    所谓“自述历史”模式(History-On-Self Pattern)实际上就是备忘录模式的一个变种。在备忘录模式中,发起人(Originator)角色、负责人(Caretaker)角色和备忘录(Memento)角色都是独立的角色。虽然在实现上备忘录类可以成为发起人类的内部成员类,但是备忘录类仍然保持作为一个角色的独立意义。在“自述历史”模式里面,发起人角色自己兼任负责人角色。

    “自述历史”模式的类图如下所示:

    备忘录角色有如下责任:

    (1)将发起人(Originator)对象的内部状态存储起来。

    (2)备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。

    发起人角色有如下责任:

    (1)创建一个含有它当前的内部状态的备忘录对象。

    (2)使用备忘录对象存储其内部状态。

    客户端角色有负责保存备忘录对象的责任。

    源代码

    窄接口MementoIF,这是一个标识接口,因此它没有定义出任何的方法。public interface MementoIF {

    }

    发起人角色同时还兼任负责人角色,也就是说它自己负责保持自己的备忘录对象。

    copycode.gifpublic class Originator {    public String state;    /**

    * 改变状态     */

    public void changeState(String state){        this.state = state;

    System.out.println("状态改变为:" + state);

    }    /**

    * 工厂方法,返还一个新的备忘录对象     */

    public Memento createMemento(){        return new Memento(this);

    }    /**

    * 将发起人恢复到备忘录对象所记录的状态上     */

    public void restoreMemento(MementoIF memento){

    Memento m = (Memento)memento;

    changeState(m.state);

    }

    private class Memento implements MementoIF{

    private String state;        /**

    * 构造方法         */

    private Memento(Originator o){            this.state = o.state;

    }        private String getState() {            return state;

    }

    }

    }

    copycode.gif

    客户端角色类

    copycode.gifpublic class Client {    public static void main(String[] args) {

    Originator o = new Originator();        //修改状态

    o.changeState("state 0");        //创建备忘录

    MementoIF memento = o.createMemento();        //修改状态

    o.changeState("state 1");        //按照备忘录恢复对象的状态        o.restoreMemento(memento);

    }

    }

    copycode.gif

    由于“自述历史”作为一个备忘录模式的特殊实现形式非常简单易懂,它可能是备忘录模式最为流行的实现形式。

    展开全文
  • 备忘录是一种行为设计模式,允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。
  • 备忘录模式在不破坏封装的前提下,记录一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态,本例中设置历次成绩,并记录和保存,最后返回最好成绩
  • 当我们在实际应用中需要提供撤销机制,当一个对象可能需要再后续操作中恢复其内部状态时,就需要使用备忘录模式。其本质就是对象的序列化和反序列化的过程,支持回滚操作。 作用 在不破坏封装性的前提下,捕获一个...
  • Java设计模式之备忘录模式

    千次阅读 2021-11-07 08:34:59
    Java设计模式之备忘录模式1. 备忘录模式概述2. 备忘录模式实现 1. 备忘录模式概述 2. 备忘录模式实现
  • 设计模式(27)之备忘录模式

    千次阅读 2021-12-08 10:45:25
    游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态。 1.1传统方式解决状态恢复问题 传统方式解决具体类图如下: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 51,413
精华内容 20,565
关键字:

备忘录模式

友情链接: excel_tool.zip