详解Java设计模式之备忘录模式的使用

定义与结构    

备忘录(Memento)模式又称标记(Token)模式。GOF给备忘录模式的定义为:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

  在讲命令模式的时候,我们曾经提到利用中间的命令角色可以实现undo、redo的功能。从定义可以看出备忘录模式是专门来存放对象历史状态的,这对于很好的实现undo、redo功能有很大的帮助。所以在命令模式中undo、redo功能可以配合备忘录模式来实现。

  其实单就实现保存一个对象在某一时刻的状态的功能,还是很简单的——将对象中要保存的属性放到一个专门管理备份的对象中,需要的时候则调用约定好的方法将备份的属性放回到原来的对象中去。但是你要好好看看为了能让你的备份对象访问到原对象中的属性,是否意味着你就要全部公开或者包内公开对象原本私有的属性呢?如果你的做法已经破坏了封装,那么就要考虑重构一下了。

  备忘录模式只是GOF对“恢复对象某时的原有状态”这一问题提出的通用方案。因此在如何保持封装性上——由于受到语言特性等因素的影响,备忘录模式并没有详细描述,只是基于C++阐述了思路。

  1) 备忘录(Memento)角色:备忘录角色存储“备忘发起角色”的内部状态。“备忘发起角色”根据需要决定备忘录角色存储“备忘发起角色”的哪些内部状态。为了防止“备忘发起角色”以外的其他对象访问备忘录。备忘录实际上有两个接口,“备忘录管理者角色”只能看到备忘录提供的窄接口——对于备忘录角色中存放的属性是不可见的。“备忘发起角色”则能够看到一个宽接口——能够得到自己放入备忘录角色中属性。

  2) 备忘发起(Originator)角色:“备忘发起角色”创建一个备忘录,用以记录当前时刻它的内部状态。在需要时使用备忘录恢复内部状态。

  3) 备忘录管理者(Caretaker)角色:负责保存好备忘录。不能对备忘录的内容进行操作或检查。

  备忘录模式的类图真是再简单不过了:

通用代码实现

class Originator {
  private String state = ""; 

  public String getState() {
    return state;
  }
  public void setState(String state) {
    this.state = state;
  }
  public Memento createMemento(){
    return new Memento(this.state);
  }
  public void restoreMemento(Memento memento){
    this.setState(memento.getState());
  }
} 

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;
  }
}
class Caretaker {
  private Memento memento;
  public Memento getMemento(){
    return memento;
  }
  public void setMemento(Memento memento){
    this.memento = memento;
  }
}
public class Client {
  public static void main(String[] args){
    Originator originator = new Originator();
    originator.setState("状态1");
    System.out.println("初始状态:"+originator.getState());
    Caretaker caretaker = new Caretaker();
    caretaker.setMemento(originator.createMemento());
    originator.setState("状态2");
    System.out.println("改变后状态:"+originator.getState());
    originator.restoreMemento(caretaker.getMemento());
    System.out.println("恢复后状态:"+originator.getState());
  }
}

代码演示了一个单状态单备份的例子,逻辑非常简单:Originator类中的state变量需要备份,以便在需要的时候恢复;Memento类中,也有一个state变量,用来存储Originator类中state变量的临时状态;而Caretaker类就是用来管理备忘录类的,用来向备忘录对象中写入状态或者取回状态。

多状态多备份备忘录
       通用代码演示的例子中,Originator类只有一个state变量需要备份,而通常情况下,发起人角色通常是一个javaBean,对象中需要备份的变量不止一个,需要备份的状态也不止一个,这就是多状态多备份备忘录。实现备忘录的方法很多,备忘录模式有很多变形和处理方式,像通用代码那样的方式一般不会用到,多数情况下的备忘录模式,是多状态多备份的。其实实现多状态多备份也很简单,最常用的方法是,我们在Memento中增加一个Map容器来存储所有的状态,在Caretaker类中同样使用一个Map容器才存储所有的备份。下面我们给出一个多状态多备份的例子:

class Originator {
  private String state1 = "";
  private String state2 = "";
  private String state3 = ""; 

  public String getState1() {
    return state1;
  }
  public void setState1(String state1) {
    this.state1 = state1;
  }
  public String getState2() {
    return state2;
  }
  public void setState2(String state2) {
    this.state2 = state2;
  }
  public String getState3() {
    return state3;
  }
  public void setState3(String state3) {
    this.state3 = state3;
  }
  public Memento createMemento(){
    return new Memento(BeanUtils.backupProp(this));
  } 

  public void restoreMemento(Memento memento){
    BeanUtils.restoreProp(this, memento.getStateMap());
  }
  public String toString(){
    return "state1="+state1+"state2="+state2+"state3="+state3;
  }
}
class Memento {
  private Map<String, Object> stateMap; 

  public Memento(Map<String, Object> map){
    this.stateMap = map;
  } 

  public Map<String, Object> getStateMap() {
    return stateMap;
  } 

  public void setStateMap(Map<String, Object> stateMap) {
    this.stateMap = stateMap;
  }
}
class BeanUtils {
  public static Map<String, Object> backupProp(Object bean){
    Map<String, Object> result = new HashMap<String, Object>();
    try{
      BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
      PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
      for(PropertyDescriptor des: descriptors){
        String fieldName = des.getName();
        Method getter = des.getReadMethod();
        Object fieldValue = getter.invoke(bean, new Object[]{});
        if(!fieldName.equalsIgnoreCase("class")){
          result.put(fieldName, fieldValue);
        }
      } 

    }catch(Exception e){
      e.printStackTrace();
    }
    return result;
  } 

  public static void restoreProp(Object bean, Map<String, Object> propMap){
    try {
      BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
      PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
      for(PropertyDescriptor des: descriptors){
        String fieldName = des.getName();
        if(propMap.containsKey(fieldName)){
          Method setter = des.getWriteMethod();
          setter.invoke(bean, new Object[]{propMap.get(fieldName)});
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
class Caretaker {
  private Map<String, Memento> memMap = new HashMap<String, Memento>();
  public Memento getMemento(String index){
    return memMap.get(index);
  } 

  public void setMemento(String index, Memento memento){
    this.memMap.put(index, memento);
  }
}
class Client {
  public static void main(String[] args){
    Originator ori = new Originator();
    Caretaker caretaker = new Caretaker();
    ori.setState1("中国");
    ori.setState2("强盛");
    ori.setState3("繁荣");
    System.out.println("===初始化状态===\n"+ori); 

    caretaker.setMemento("001",ori.createMemento());
    ori.setState1("软件");
    ori.setState2("架构");
    ori.setState3("优秀");
    System.out.println("===修改后状态===\n"+ori); 

    ori.restoreMemento(caretaker.getMemento("001"));
    System.out.println("===恢复后状态===\n"+ori);
  }
}

备忘录模式的优缺点和适用场景
备忘录模式的优点有:
当发起人角色中的状态改变时,有可能这是个错误的改变,我们使用备忘录模式就可以把这个错误的改变还原。
备份的状态是保存在发起人角色之外的,这样,发起人角色就不需要对各个备份的状态进行管理。
备忘录模式的缺点:
在实际应用中,备忘录模式都是多状态和多备份的,发起人角色的状态需要存储到备忘录对象中,对资源的消耗是比较严重的。
如果有需要提供回滚操作的需求,使用备忘录模式非常适合,比如jdbc的事务操作,文本编辑器的Ctrl+Z恢复等。

(0)

相关推荐

  • Java设计模式之备忘录模式(Memento模式)介绍

    Memento定义:memento是一个保存另外一个对象内部状态拷贝的对象,这样以后就可以将该对象恢复到原先保存的状态. Memento模式相对也比较好理解,我们看下列代码: 复制代码 代码如下: public class Originator { private int number; private File file = null; public Originator(){} // 创建一个Memento public Memento getMemento(){ return new Me

  • Java设计模式之备忘录模式_动力节点Java学院

    定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样就可以将该对象恢复到原先保存的状态 类型:行为类 类图: 我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回.这时我们便可以使用备忘录模式来实现. 备忘录模式的结构 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和

  • 详解备忘录模式及其在Java设计模式编程中的实现

    1. 定义 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. 2. 使用的原因 想要恢复对象某时的原有状态. 3. 适用的情况举例 有很多备忘录模式的应用,只是我们已经见过,却没细想这是备忘录模式的使用罢了,略略举几例: eg1. 备忘录在jsp+javabean的使用: 在一系统中新增帐户时,在表单中需要填写用户名.密码.联系电话.地址等信息,如果有些字段没有填写或填写错误,当用户点击"提交"按钮时,需要在新增页面

  • JAVA设计模式之备忘录模式原理与用法详解

    本文实例讲述了JAVA设计模式之备忘录模式.分享给大家供大家参考,具体如下: 备忘录模式:又叫做快照模式,指在不破坏封装性的前提下,获取到一个对象的内部状态,并在对象之外记录或保存这个状态.在有需要的时候可将该对象恢复到原先保存的状态.我们相当于把对象原始状备份保留,所以叫备忘录模式. *模式 角色对象组成: 1.发起者对象:负责创建一个备忘录来记录当前对象的内部状态,并可使用备忘录恢复内部状态. 2.备忘录对象:负责存储发起者对象的内部状态,并防止其他对象访问备忘录. 3.管理者对象:负责备忘

  • 详解Java设计模式之备忘录模式的使用

    定义与结构     备忘录(Memento)模式又称标记(Token)模式.GOF给备忘录模式的定义为:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. 在讲命令模式的时候,我们曾经提到利用中间的命令角色可以实现undo.redo的功能.从定义可以看出备忘录模式是专门来存放对象历史状态的,这对于很好的实现undo.redo功能有很大的帮助.所以在命令模式中undo.redo功能可以配合备忘录模式来实现. 其实单就实现保存一个对

  • 详解JAVA设计模式之代理模式

    什么是设计模式(Design Pattern)? 设计模式是一套被反复使用,多数人知晓的,经过分类编目的,代码设计经验的总结. 代理模式的定义? 代理模式就是为其他对象提供一种代理,以控制对这个对象的访问. 代理对象起到中介作用,可去掉功能服务或增加额外的服务. 代理对象和目标对象的关系? 代理对象:增强后的对象 目标对象:被增强的对象 他们不是绝对的,会根据情况发生变化. 代理模式的两种实现方式? 1.静态代理:代理和被代理对象在代理之前是确定的,它们都实现相同的接口或者继承相同的抽象类. 2

  • 详解JAVA设计模式之模板模式

    在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板.它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行.这种类型的设计模式属于行为型模式. 介绍 意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 主要解决:一些方法通用,却在每一个子类都重新写了这一方法. 何时使用:有一些通用的方法. 如何解决:将这些通用算法抽象出来. 关键代码:在抽象类实现,其他步骤在子

  • 详解JAVA 设计模式之状态模式

    在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式. 在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象. 介绍 意图: 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类. 主要解决: 对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为. 何时使用: 代码中包含大量与对象状态有关的条件语句. 如何解决: 将各种具体的状态类抽象出来. 关键代码: 通常

  • 详解Java设计模式之桥接模式

    目录 一.什么是桥接模式: 二.UML结构图 三.代码实现 四.JDBC源码解析-桥接模式 1.源码分析 2.源码类 3.对 JDBC 的观点 一.什么是桥接模式: 桥接,顾名思义,就是用来连接两个部分,使得两个部分可以互相通讯,桥接模式的作用就是为被分离的抽象部分和实现部分搭桥.在现实生活中一个物品在搭配不同的配件时会产生不同的动作和结果,例如一辆赛车搭配的是硬胎或者是软胎就能够在干燥的马路上行驶,而如果要在下雨的路面行驶,就需要搭配雨胎了,这种根据行驶的路面不同,需要搭配不同的轮胎的变化的情

  • 详解JAVA设计模式之适配器模式

    适配器模式 适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁.这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能. 这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能.举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器.您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡. 我们通过下面的实例来演示适配器模式的使用.其中,音频播放器设备只能播放 mp3 文件,通过使用一个更高级的音频播放器来播放 vlc 和 mp4

  • 详解java设计模式之六大原则

    一.单一职责原则 1.单一职责定义 单一职责原则:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因. 单一职责原则告诉我们:一个类不能太"累"!在软件系统中,一个类承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责 变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封

  • 详解Java从工厂方法模式到 IOC/DI思想

    前言 简单工厂的本质是选择实现,说白了是由一个专门的类去负责生产我们所需要的对象,从而将对象的创建从代码中剥离出来,实现松耦合.我们来看一个例子: 我们要创建一个文件导出工具 public interface FileOper{ public Boolean exceptFile(String data); } public class XMLFileOp implment FileOper{ public Boolean exceptFile(String data){ System.out.

  • 详解Java实践之建造者模式

    目录 一.前言 二.开发环境 三.建造者模式介绍 四.案例场景模拟 4.1.场景模拟工程 4.2.场景简述 4.2.1.物料接口 4.2.2.吊顶(ceiling) 4.2.3.涂料(coat) 4.2.4.地板(floor) 4.2.5.地砖(tile) 五.代码实现 5.1.工程结构 5.2.ifelse实现需求 5.3. 测试验证 六.建造者模式重构代码 6.1.工程结构 6.2.代码实现 6.2.1.定义装修包接口 6.2.2.装修包实现 6.2.3.建造者方法 6.3.测试验证 七.总

随机推荐