备忘录模式

1.模式简介

      备忘录模式可以在不破坏封装性的前提下,实现对象状态的保存和恢复工做,又叫快照模式或Token模式。保存对象的状态是为了之后在须要的时候快速恢复到保存时的状态,所以经常使用在备份、撤销操做上,例如编辑器里的撤销、游戏里的存档和悔棋等功能。安全

      备忘录模式有三个组成部分:编辑器

      Originagor(发起人):即须要备份的对象,能够建立备忘录,以及根据备忘录来恢复状态,能够看到备忘录提供的宽接口。ide

      Memento(备忘录):存储Originator的部分或全部状态,对外提供宽窄接口。测试

      CareTaker(管理人):负责保存Memento对象,只能看到备忘录提供的窄接口。this

      上面提到了宽接口和窄接口,有必要先解释一下,宽窄接口实际上表明了外界对备忘录的访问权限问题:spa

      宽接口:可以看到备忘录保存的全部数据,通常只对发起人可见,对其余角色不可见。rest

      窄接口:只能看到备忘录保存的部分数据(甚至能够实现不对外暴露任何数据),一般出于封装和安全性考虑,对发起人以外的其余角色只提供窄接口。code

2. 示例

      下面以一个简单的例子演示备忘录模式的用法,示例模仿棋类游戏中的悔棋,为简单起见,只记录棋子的坐标。对象

       先定义棋子类Chessman,包含棋子的x坐标和y坐标:blog

public class Chessman {
  private int positionx;
  private int positiony;

  public void setPosition(int positionx, int positiony) {
    this.positionx = positionx;
    this.positiony = positiony;
  }

  @Override
  public String toString() {
    return "当前位置{" +
      "positionx=" + positionx +
      ", positiony=" + positiony +
      '}';
  }

  public Chessman(int x, int y){
    this.positionx = x;
    this.positiony = y;
  }

  public Memento createMemento(){
    return new Memento(positionx, positiony);
  }

  public void restore(Memento memento){
    this.positionx = memento.getPositionx();
    this.positiony = memento.getPositiony();
  }
}

      接着定义备忘录类Memento,用来存储棋子的坐标信息:

public class Memento {
  private int positionx;
  private int positiony;

  public int getPositionx() {
    return positionx;
  }

  public int getPositiony() {
    return positiony;
  }

  public Memento(int x, int y){
    this.positionx = x;
    this.positiony = y;
  }
}

      定义管理者类CareTaker,外界经过该类获取备份信息:

public class CareTaker {
  private Memento memento;

  public Memento getMemento() {
    return memento;
  }

  public void setMemento(Memento memento) {
    this.memento = memento;
  }
}

      接下来用客户端来测试这个简单的备忘录:

public class MementoTest {
  public static void main(String[] args) {
    Chessman chessman = new Chessman(0,0);
    chessman.setPosition(3,4);
    System.out.println(chessman);
    CareTaker careTaker = new CareTaker();
    System.out.println("备份棋子位置。。。");
    careTaker.setMemento(chessman.createMemento());
    chessman.setPosition(7,5);
    System.out.println(chessman);
    System.out.println("悔棋。。。");
    chessman.restore(careTaker.getMemento());
    System.out.println(chessman);
  }
}

      输出为:

当前位置{positionx=3, positiony=4}
备份棋子位置。。。
当前位置{positionx=7, positiony=5}
悔棋。。。
当前位置{positionx=3, positiony=4}

      该示例所对应的类图结构以下:

      上面这个示例只是单备份,也就是说只能备份一个状态,将CareTaker中的Memento修改为集合的形式能够实现多备份。

3. “黑箱”备忘录模式

      其实上面的实现方式有一个很大的问题,就是Memento对全部的外界对象都是公开的,任何对象均可以访问和修改Memento的字段,这种模式称为“白箱”模式。因为没有相应的权限控制,这种方式没法保证备忘录的安全性,不具有太大的实用价值。一种解决方案是将Memento设置为Originator的内部类,并经过权限控制符来限制外界对他的访问。

      修改后的Chessman类,拥有私有的Memebto类:

public class ChessmanNew {
  private int positionx;
  private int positiony;

  public void setPosition(int positionx, int positiony) {
    this.positionx = positionx;
    this.positiony = positiony;
  }

  @Override
  public String toString() {
    return "当前位置{" +
      "positionx=" + positionx +
      ", positiony=" + positiony +
      '}';
  }

  public ChessmanNew(int x, int y){
    this.positionx = x;
    this.positiony = y;
  }

  public MementoFace createMemento(){
    return new Memento(positionx, positiony);
  }

  public void restore(MementoFace memento){
    this.positionx = memento.getx();
    this.positiony = ((Memento)memento).getPositiony();
  }

  private class Memento implements MementoFace{
    private int positionx;
    private int positiony;

    private Memento(int x, int y){
      this.positionx = x;
      this.positiony = y;
    }

    private int getPositiony(){
      return this.positiony;
    }

    @Override
    public int getx() {
      return this.positionx;
    }
  }
}

      Memento对外以接口MementoFace的形式提供有限的服务(即只容许外界访问x坐标,而对外隐藏y坐标),MementoFace的定义以下:

public interface MementoFace {
  //窄接口
  int getx();
}

      CareTaker类也须要作对应的修改:

public class CareTakerNew {
  private MementoFace mementoFace;

  public MementoFace getMementoFace() {
    return mementoFace;
  }

  public void setMementoFace(MementoFace mementoFace) {
    this.mementoFace = mementoFace;
  }
}

      客户端测试以下:

public class MementoTest {
  public static void main(String[] args) {
    newtest();
  }

  public static void newtest(){
    ChessmanNew chessman = new ChessmanNew(0,0);
    chessman.setPosition(3,4);
    System.out.println(chessman);
    System.out.println("备份棋子位置。。。");
    CareTakerNew careTaker = new CareTakerNew();
    careTaker.setMementoFace(chessman.createMemento());
    System.out.println(chessman);
    System.out.println("悔棋。。。");
    chessman.restore(careTaker.getMementoFace());
    System.out.println(chessman);
  }
}

      输出与修改前的代码一致,这里略去。这种方式修改后,外界可以接触到的只有MementoFace接口,只能访问x坐标,其余什么也作不了,从而保证了封装性。

相关文章
相关标签/搜索