Java设计模式(十九)----备忘录模式

备忘录模式
一、 概念
二、 结构
三、 分类
1.”白箱”备忘录模式的实现
2.“黑箱”备忘录模式的实现
3.“多重”检查点
4.”自述历史”模式



引子
我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状 态。比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返 回。下象棋的时候,可以反悔。这时我们便可以使用备忘录模式来实现。

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



二、结构

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

备忘录角色
  备忘录角色又如下责任:
  (1)将发起人(Originator)对象的内战状态存储起来。备忘录可以根据发起人对象的判断来决定存储多少发起人(Originator)对象的内部状态。
  (2)备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。
  备忘录有两个等效的接口:
  ●  窄接口:负责人(Caretaker)对象(和其他除发起人对象之外的任何对象)看到的是备忘录的窄接口(narrow interface),这个窄接口只允许它把备忘录对象传给其他的对象。
  ●  宽接口:与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口(wide interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。
发起人角色
  发起人角色有如下责任:
  (1)创建一个含有当前的内部状态的备忘录对象。
  (2)使用备忘录对象存储其内部状态。
负责人角色
  负责人角色有如下责任:
  (1)负责保存备忘录对象。
(2)不检查备忘录对象的内容。



三、 分类
1.”白箱”备忘录模式的实现
  备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开。因此这个实现又叫做“白箱实现”。
  “白箱”实现将发起人角色的状态存储在一个大家都看得到的地方,因此是破坏封装性的。但是通过程序员自律,同样可以在一定程度上实现模式的大部分用意。因此白箱实现仍然是有意义的。
下面给出一个示意性的“白箱实现”。


  
源代码

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

package Memento;

public 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);
    }

}


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

package Memento;

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;
    }

}


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

package Memento;

public class Caretaker {

    private Memento memento;

    /**
     * 备忘录的取值方法
     */
    public Memento retrieveMemento() {
        return this.memento;
    }

    /**
     * 备忘录的赋值方法
     */
    public void saveMemento(Memento memento) {
        this.memento = memento;
    }
}


 客户端角色类

package Memento;

public 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());
    }

}

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



2.“黑箱”备忘录模式的实现
  备忘录角色对发起人(Originator)角色对象提供一个宽接口,而为其他对象提供一个窄接口。这样的实现叫做“黑箱实现”。
  在JAVA语言中,实现双重接口的办法就是将备忘录角色类设计成发起人角色类的内部成员类。
  将Memento设成Originator类的内部类,从而将Memento对象封装在Originator里面;在外部提供一个标识接口 MementoIF给Caretaker以及其他对象。这样,Originator类看到的是Menmento的所有接口,而Caretaker以及其他 对象看到的仅仅是标识接口MementoIF所暴露出来的接口。
  使用内部类实现备忘录模式的类图如下所示。

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

 package Memento1;

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;
        }
    }
}

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

 package Memento1;

public interface MementoIF {

}

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

package Memento1;

public class Caretaker {

    private MementoIF memento;

    /**
     * 备忘录取值方法
     */
    public MementoIF retrieveMemento() {
        return memento;
    }

    /**
     * 备忘录赋值方法
     */
    public void saveMemento(MementoIF memento) {
        this.memento = memento;
    }
}

客户端角色类

package Memento1;

public 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());
    }

}


结果:
赋值状态:On
赋值状态:Off
赋值状态:On

客户端首先
  (1)将发起人对象的状态设置为“On”。
  (2)调用createMemento()方法,创建一个备忘录对象将这个状态存储起来(此时createMemento()方法还回的明显类型是MementoIF接口,真实类型为Originator内部的Memento对象)。
  (3)将备忘录对象存储到负责人对象中去。由于负责人对象拿到的仅是MementoIF接口,因此无法读出备忘录对象内部的状态。
  (4)将发起人对象的状态设置为“Off”。
  (5)调用负责人对象的retrieveMemento()方法将备忘录对象取出。注意此时仅能得到MementoIF接口,因此无法读出此对象的内部状态。
  (6)调用发起人对象的restoreMemento()方法将 发起人对象的状态恢复成备忘录对象所存储的起来的状态,即“On”状态。由于发起人对象的内部类Memento实现了MementoIF接口,这个内部类 是传入的备忘录对象的真实类型,因此发起人对象可以利用内部类Memento的私有接口读出此对象的内部状态。



 3.多重检查点
  前面所给出的白箱和黑箱的示意性实现都是只存储一个状态的简单实现,也可以叫做只有一个检查点。常见的系统往往需要存储不止一个状态,而是需要存储多个状态,或者叫做有多个检查点。
  备忘录模式可以将发起人对象的状态存储到备忘录对象里面,备忘录模式可以将发起人对象恢复到备忘录对象所存储的某一个检查点上。下面给出一个示意性的、有多重检查点的备忘录模式的实现。

源代码
发起人角色源代码

package Memento2;

import java.util.ArrayList;
import java.util.List;

public class Originator {

    private List<String> states;
    // 检查点指数
    private int index;

    /**
     * 构造函数
     */
    public Originator() {
        states = new ArrayList<String>();
        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);
        }
    }
}

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

package Memento2;

import java.util.ArrayList;
import java.util.List;

public class Memento {

    private List<String> states;
    private int index;
    /**
     * 构造函数
     */
    public Memento(List<String> states , int index){
        this.states = new ArrayList<String>(states);
        this.index = index;
    }
    public List<String> getStates() {
        return states;
    }
    public int getIndex() {
        return index;
    }

}

负责人角色类

package Memento2;

import java.util.ArrayList;
import java.util.List;

public class Caretaker {

    private Originator o;
    private List<Memento> mementos = new ArrayList<Memento>();
    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);
    }
}

客户端角色源代码

package Memento2;

public 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();
    }

}

运行结果如下:
state 0
state 1
state 2
state 3
—————–恢复检查点—————–
state 0
state 1
state 2

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

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

备忘录角色有如下责任:
  (1)将发起人(Originator)对象的内部状态存储起来。
  (2)备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。
 发起人角色有如下责任:
  (1)创建一个含有它当前的内部状态的备忘录对象。
  (2)使用备忘录对象存储其内部状态。
  客户端角色有负责保存备忘录对象的责任。

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

public interface MementoIF {

}

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

package Memento3;

public 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;
        }

    }

}

客户端角色类

package Memento3;

public 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);
    }

}

结果:
状态改变为:state 0
状态改变为:state 1
状态改变为:state 0

 由于“自述历史”作为一个备忘录模式的特殊实现形式非常简单易懂,它可能是备忘录模式最为流行的实现形式。
 
以上内容来自平时所看书籍和网络资源整理测试所得,如有不完善之处,欢迎指正!

时间: 2024-10-26 06:01:14

Java设计模式(十九)----备忘录模式的相关文章

设计模式 ( 十九 ) 模板方法模式Template method(类行为型)

设计模式 ( 十九 ) 模板方法模式Template method(类行为型) 1.概述 在面向对象开发过程中,通常我们会遇到这样的一个问题:我们知道一个算法所需的关键步骤,并确定了这些步骤的执行顺序.但是某些步骤的具体实现是未知的,或者说某些步骤的实现与具体的环境相关. 例子1:银行业务办理流程 在银行办理业务时,一般都包含几个基本固定步骤: 取号排队->办理具体业务->对银行工作人员进行评分. 取号取号排队和对银行工作人员进行评分业务逻辑是一样的.但是办理具体业务是个不相同的,具体业务可能

设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述         在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查找.排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法:当然也可以将这些查找算法封装在一个统一的方法中,通过if-else-或者case等条件判断语

Java设计模式之中介者模式(Mediator Pattern)简介_java

Java设计模式的中介者模式是行为模式之一.定义一个中介对象来封装系列对象之间的交互.中介者使各个对象不需要显式地相互引用,从而使其耦合性松散,并且可以独立地改变他们之间的交互. 如下图所示: 生产者.消费者.代理商之间的中介模式实例: public abstract class PersonColleague { protected Mediator mediator; public Mediator getMediator() { return mediator; } public void

java设计模式之简单工厂模式

在编写一个计算器程序时,可以将业务逻辑和显示分离,业务逻辑封装为一个类(封装):如果要新添加一种运算,可以先创建一个Operation的基类,然后各种运算从Operation类继承,并实现GetResult()虚函数,这时添加新的运算只需要派生一个新的类,即不需要之前的运算参与编译.如何让计算器知道我是希望使用哪种运算呢?应该考虑单独的类来做这个创造实例的过程,这就是工厂.创建一个OperationFactory类,传入参数,函数createOperate就可以实例化出合适的对象. Java代码

设计模式中的备忘录模式解析及相关C++实例应用_C 语言

备忘录模式旨在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态.在命令模式中,备忘录模式经常还经常被用来维护可以撤销(Undo)操作的状态. 类图: Originator:负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态.Originator可根据需要决定Memento存储Originator的哪些内部状态. Memento:负责存储Originator对象的内部状态,并可防止Origin

java设计模式之工厂方法模式_java

概要 设计模式是一门艺术,如果真正了解这门艺术,你会发现,世界都将变得更加优美. 定义 定义一个用于创建对象的接口,让其子类去决定实例化那个类 使用场景 任何使用复杂对象的地方,都可以使用工厂模式 UML 1.抽象工厂  抽象工厂:我们都知道,工厂,一般只有一个作用,那就生产,比如,吉利汽车厂,那就制造吉利汽车,iphone手机制造厂就造iphone等等 所以可以用一个简单的方法概括,就是create(); 2.具体汽车制造厂  具体汽车制造厂:实现了抽象工厂,具有实际的制造汽车的流程和方法等

Java设计模式之享元模式学习笔记

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能.这种这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式.享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象. Java中String的实现就是一个典型的享元模式应用,Java中的String存在字符串常量池中,Java会确保一个字符串常量在常量池中只有一个拷贝.数据库连接池也是一个比较电信的享元模式应用,可简单理解为先初始化一定数量的数据库连接,

解析Java设计模式编程中命令模式的使用_java

定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能.类型:行为类模式类图: 命令模式的结构        顾名思义,命令模式就是对命令的封装,首先来看一下命令模式类图中的基本结构: Command类:是一个抽象类,类中对需要执行的命令进行声明,一般来说要对外公布一个execute方法用来执行命令. ConcreteCommand类:Command类的实现类,对抽象类中声明的方法进行实现. Client类:最终的客户端调用

Java设计模式之责任链模式简介_java

对于使用过宏的朋友应该知道,利用宏可以实现一个键绑定多个技能.例如如果排在前面的技能有CD,则跳过此技能,执行之后的技能.记得曾经玩DK,打怪的时候,就是用一个键,一直按就行了.在servlet里的doGet和doPost方法,我们一般都把doGet请求发动到doPost里来处理,这也是一种责任链的模式. 这里,有个宏,绑定了"冰血冷脉"和"寒冰箭"两个技能,程序实例如下所示: package responsibility; /** * DOC 技能接口,要绑定的技

java设计模式之中介者模式_java

中介者模式 面向对象设计鼓励将行为分布到各个对象中, 这种分布可能会导致对象间有许多连接. 在最坏的情况下, 每一个对象都需要知道其他所有对象. 虽然将一个系统分割成许多对象可增强可复用性, 但是对象间相互连接的激增又会降低其可复用性. 大量的连接关系使得一个对象不可能在没有其他对象的协助下工作(系统表现为一个不可分割的整体), 此时再对系统行为进行任何较大改动就十分困难. 因为行为被分布在许多对象中, 结果是不得不定义很多子类以定制系统的行为. 由此我们引入了中介者对象Mediator: