18. 备忘录模式

18 备忘录模式

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

  2. 理解

    1. Originator:原发器,使用备忘录来保存某个时刻原发器自身的状态,也可以使用备忘录来恢复内部状态
    2. Memento:备忘录,存储原发器对象的内部状态,原发器外部的对象不应该能访问到备忘录对象的内部数据
    3. Caretaker:备忘录管理者,主要负责保存备忘录对象,但是没有权限对备忘录对象的内容进行操作或检查
  3. 优点

    1. 更好的封装性:外界无法访问Memento中细节,只有Originator内部可以
    2. 简化了原发器:备忘录对象被保存到原发器对象之外,让客户来管理他们请求的状态,从而让原发器对象得到简化
    3. 窄接口和宽接口:备忘录模式,通过引入窄接口和宽接口,使得不同的地方,对备忘录对象的访问是不一样的。窄接口保证了只有原发器才可以访问备忘录对象的状态
  4. 缺点

    1. 可能会导致高开销:备忘录模式基本的功能,就是对备忘录对象的存储和恢复,它的基本实现方式就是缓存备忘录对象。这样一来,如果需要缓存的数据量很大,或者是特别频繁的创建备忘录对象,开销是很大的
  5. 本质:保存和恢复内部状态

  6. 使用场景

    1. 如果必须保存一个对象在某一个时刻的全部或者部分状态,这样在以后需要的时候,可以把该对象恢复到先前的状态。可以使用备忘录模式,使用备忘录对象来封装和保存需要保存的内部状态,然后把备忘录对象保存到管理者对象里面,在需要的时候,再从管理者对象里面获取备忘录对象,来恢复对象的状态
    2. 如果需要保存一个对象的内部状态,但是如果用接口来让其它对象直接得到这些需要保存的状态,将会暴露对象的实现细节并破坏对象的封装性。可以使用备忘录模式,把备忘录对象实现成为原发器对象的内部类,而且还是私有的,从而保证只有原发器对象才能访问该备忘录对象。这样既保存了需要保存的状态,又不会暴露原发器对象的内部实现细节
  7. 类图
    在这里插入图片描述

18.1 代码
  1. FlowAMockMemento:Memento

    /**
     * 模拟运行流程A的对象的备忘录接口,是个窄接口
     */
    public interface FlowAMockMemento {
        //空的
    }
    
  2. FlowAMock:Originator

    /**
     * 模拟运行流程A,只是一个示意,代指某个具体流程
     */
    public class FlowAMock {
        /**
         * 流程名称,不需要外部存储的状态数据
         */
        private String flowName;
        /**
         * 示意,代指某个中间结果,需要外部存储的状态数据
         */
        private int tempResult;
        /**
         * 示意,代指某个中间结果,需要外部存储的状态数据
         */
        private String tempState;
        /**
         * 构造方法,传入流程名称
         * @param flowName 流程名称
         */
        public FlowAMock(String flowName){
            this.flowName = flowName;
        }
        /**
         * 示意,运行流程的第一个阶段
         */
        public void runPhaseOne(){
            //在这个阶段,可能产生了中间结果,示意一下
            tempResult = 3;
            tempState = "PhaseOne";
        }
        /**
         * 示意,按照方案一来运行流程后半部分
         */
        public void schema1(){
            //示意,需要使用第一个阶段产生的数据
            this.tempState += ",Schema1";
            System.out.println(this.tempState + " : now run "+tempResult);
            this.tempResult += 11;
        }
        /**
         * 示意,按照方案二来运行流程后半部分
         */
        public void schema2(){
            //示意,需要使用第一个阶段产生的数据
            this.tempState += ",Schema2";
            System.out.println(this.tempState + " : now run "+tempResult);
            this.tempResult += 22;
        }
        /**
         * 创建保存原发器对象的状态的备忘录对象
         * @return 创建好的备忘录对象
         */
        public FlowAMockMemento createMemento() {
            return new MementoImpl(this.tempResult,this.tempState);
        }
        /**
         * 重新设置原发器对象的状态,让其回到备忘录对象记录的状态
         * @param memento 记录有原发器状态的备忘录对象
         */
        public void setMemento(FlowAMockMemento memento) {
            MementoImpl mementoImpl = (MementoImpl)memento;
            this.tempResult = mementoImpl.getTempResult();
            this.tempState = mementoImpl.getTempState();
        }
        /**
         * 真正的备忘录对象,实现备忘录窄接口
         * 实现成私有的内部类,不让外部访问
         */
        private static class MementoImpl implements FlowAMockMemento{
            /**
             * 示意,保存某个中间结果
             */
            private int tempResult;
            /**
             * 示意,保存某个中间结果
             */
            private String tempState;
            public MementoImpl(int tempResult,String tempState){
                this.tempResult = tempResult;
                this.tempState = tempState;
            }
            public int getTempResult() {
                return tempResult;
            }
            public String getTempState() {
                return tempState;
            }
        }
    }
    
  3. FlowAMementoCareTaker:CareTaker

    /**
     * 负责保存模拟运行流程A的对象的备忘录对象
     */
    public class FlowAMementoCareTaker {
        /**
         * 记录被保存的备忘录对象
         */
        private FlowAMockMemento memento = null;
        /**
         * 保存备忘录对象
         * @param memento 被保存的备忘录对象
         */
        public void saveMemento(FlowAMockMemento memento){
            this.memento = memento;
        }
        /**
         * 获取被保存的备忘录对象
         * @return 被保存的备忘录对象
         */
        public FlowAMockMemento retriveMemento(){
            return this.memento;
        }
    }
    
  4. Client

    public class Client {
        public static void main(String[] args) {
            // 创建模拟运行流程的对象
            FlowAMock mock = new FlowAMock("TestFlow");
            //运行流程的第一个阶段
            mock.runPhaseOne();
            //创建一个管理者
            FlowAMementoCareTaker careTaker = new FlowAMementoCareTaker();
            //创建此时对象的备忘录对象,并保存到管理者对象那里,后面要用
            FlowAMockMemento memento = mock.createMemento();
            careTaker.saveMemento(memento);
            //按照方案一来运行流程后半部分
            mock.schema1();
    
            //从管理者获取备忘录对象,然后设置回去,
            //让模拟运行流程的对象自己恢复自己的内部状态
            mock.setMemento(careTaker.retriveMemento());
    
            //按照方案二来运行流程后半部分
            mock.schema2();
        }
    }
    
18.2 双重接口问题
  1. 所谓双重接口,就是对某一个对象提供宽接口,而对另一个对象提供窄接口
  2. 案例中,对CareTaker提供的是窄接口FlowAMockMemento,无法访问对象的任何内容,对FlowAMock提供的是宽接口MementoImpl,因此其内部可以访问MementoImpl的方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值