• 备忘录模式


    1.模式简介

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

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

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

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

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

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

          宽接口:能够看到备忘录保存的所有数据,一般只对发起人可见,对其他角色不可见。

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

    2. 示例

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

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

    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坐标,其他什么也做不了,从而保证了封装性。

  • 相关阅读:
    spring常用注解
    P1255 数楼梯
    蓝桥杯 传纸条(动态规划)
    蓝桥杯 数的划分两种解法
    蓝桥杯 数独
    Elasticsearch05-批量增删改查
    Elasticsearch04-正排索引和倒排索引
    Elasticsearch03-Mapping和聚合
    Elasticsearch02-查询语法
    亿级流量多级缓存高并发系统架构实战
  • 原文地址:https://www.cnblogs.com/NaLanZiYi-LinEr/p/11966835.html
Copyright © 2020-2023  润新知