兄弟们,老铁们.又到了学习锻炼我们可爱小小大脑的时候了~~~~~ 今天继续来学习设计模式,正所谓一天一个效果不错!!
喝了这碗鸡血,学就完了~~~
执着的攀登者不必去与别人比较自己的形象是否高大,重要的是要多多思考自己前进的脚步是否扎实。

1.备忘录模式定义

备忘录模式又称标记模式.GOF给的定义为:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态.
就像<<大话西游>>中能使时光倒流的"月光宝盒"一样,回到过去.

2. 备忘录模式结构

java备忘录设计 java备忘录模式_java备忘录设计

  • 备忘录(Memento)角色:备忘录角色存储“备忘发起角色”的内部状态。“备忘发起角色”根据需要决定备忘录角色存储“备忘发起角色”的哪些内部状态。为了防止“备忘发起角色”以外的其他对象访问备忘录。备忘录实际上有两个接口,“备忘录管理者角色”只能看到备忘录提供的窄接口——对于备忘录角色中存放的属性是不可见的。 “备忘发起角色”则能够看到一个宽接口——能够得到自己放入备忘录角色中属性。
  • 备忘发起(Originator)角色:“备忘发起角色”创建一个备忘录,用以记录当前时刻它的内部状态。在需要时使用备忘录恢复内部状态。
  • 备忘录管理者(Caretaker)角色:负责保存好备忘录。不能对备忘录的内容进行操作或检查。

窄接口:管理者(Caretaker)对象(和其他除发起人对象之外的任何对象)看到的是备忘录的窄接口(narrow interface),这个窄接口只允许它把备忘录对象传给其他的对象。
宽接口:与管理者对象看到的窄接口相反的是,发起人对象可以看到一个宽接口(wide interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。

3.备忘录模式实现

3.1 白箱

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

"白箱"将发起人角色的状态都存在了一个谁都能看得到的地方,因此破坏了封装性.这种方法实现比较简单,但需要人为的进行规范约束.

java备忘录设计 java备忘录模式_java_02

负责人角色类

public class Caretaker {

  private Memento memento;
  /**
   * 备忘录的取值方法
   */
  public Memento retrieveMemento(){
    return this.memento;
  }
  /**
   * 备忘录的赋值方法
   */
  public void saveMemento(Memento memento){
    this.memento = 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;
  }

}

发起人角色

//发起人角色
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);
  }

}

客户端测试

public static void main(String[] args) {
    Originator o = new Originator();
    Caretaker c = new Caretaker();
    //改变负责人对象的状态
    o.setState("On");
    //创建备忘录对象,并将发起人对象的状态储存起来
    c.saveMemento(o.createMemento());
    //修改发起人的状态
    o.setState("Off");
    //恢复发起人对象的状态
    System.out.println("恢复发起人状态");
    o.restoreMemento(c.retrieveMemento());

    System.out.println( "当前状态 :" + o.getState());
  }

结果

当前状态:On
当前状态:Off
恢复发起人状态
On

3.2 黑箱

备忘录角色对发起人(Originator)角色对象提供一个宽接口,而为其他对象提供一个窄接口。这样的实现叫做“黑箱实现”。
将备忘录角色设成发起人类的内部类,从而将备忘录角色对象封装在发起人里面;在外部提供一个标识接口MementoIF给负责人以及其他对象。这样,发起人类看到的是备忘录角色的所有接口,而负责人以及其他对象看到的仅仅是标识接口MementoIF所暴露出来的接口。

java备忘录设计 java备忘录模式_java备忘录设计_03

窄接口

public interface MementoIF {

}

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

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为接口的,由于这个接口仅仅是一个标识接口,因此负责人角色不可能改变这个备忘录对象的内容。

public class Caretaker {

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

}

客户类测试

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

黑箱很好的解决了白箱的缺陷:采用内部类来控制访问权限.

4.适用场景

  • 必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。
  • 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象
    的封装性。