(18)备忘录模式



(18)备忘录模式

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

类型:行为类

类图:

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

备忘录模式的结构

  • 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
  • 备忘录:负责存储发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
  • 管理角色:对备忘录进行管理,保存和提供备忘录。

通用代码实现

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恢复等。

时间: 2024-10-18 23:30:40

(18)备忘录模式的相关文章

设计模式(18)-----备忘录模式

备忘录模式(memento) 定义 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象回复到原先保存的状态. UML图 角色说明 备忘录(Memento)角色 (1)将发起人(Originator)对象的内战状态存储起来.备忘录可以根据发起人对象的判断来决定存储多少发起人(Originator)对象的内部状态. (2)备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取. 发起人(Originator)角色 (1)创建一个含

笔记-大话设计模式-18 备忘录模式

备忘录模式(Memento),在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态.Originator可根据需要决定Memento存储Originator的哪些内部状态. Memento(备忘录):负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento

18.备忘录模式

备忘录模式就是保存某个对象内部状态的拷贝,这样就可以将该对象恢复为原来的对象. 结构: 源发器类:备忘录类:负责人类: //源发器类public class Emo { private String ename; private int age; private String salary; //进行备忘录操作 public EmpMemento memento(){ return new EmpMemento(this); } //进行数据恢复 public void recovery(Emp

第18章 备忘录模式(Memento Pattern)

原文  第18章 备忘录模式(Memento Pattern) 备忘录模式       概述: 备忘录模式(Memento Pattern)又叫做快照模式(Snapshot Pattern)或Token模式,是GoF的23种设计模式之一,属于行为模式. 定义(源于GoF<设计模式>):在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. 结构图:        代码举例: 1 2 3 4 5 6 7 8 9 10 11 12 13

大话设计模式C++实现-第18章-备忘录模式

一.UML图 二.概念 备忘录(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将对象恢复到原先保存的状态. 三.说明 角色: (1)Originator(发起人):负责创建一个Memento,用以记录当前时刻它的内部状态,并可以使用备忘录恢复内部状态.Originator可以根据需要决定Memento存储Originator的哪些内部状态. (2)Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Origi

[Python设计模式] 第18章 游戏角色备份——备忘录模式

github地址:https://github.com/cheesezh/python_design_patterns 题目 用代码模拟以下场景,一个游戏角色有生命力,攻击力,防御力等数据,在打Boss前后的数据值会发生变化,如果玩家挑战Boss失败,我们允许玩家可以将游戏数据恢复到与Boss决斗之前的状态. 基础版本 class GameRole(): def __init__(self): self.vitality = 0 self.attack = 0 self.defense = 0

4 行为型模式之- 备忘录模式

备忘录模式介绍: 备忘录模式是一种行为模式,该模式用于保存对象当前的状态,并且在之后可以再次恢复到此状态,这有点像我们平时说的"后悔"药.备忘录模式实现的方式需要保证被保存的对象状态不能被对象从外界访问,目的是为了保护好被保存的这些对象状态的完整性以及内部实现不向外暴露 备忘录模式的定义: 在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可以将对象恢复到原先的状态 对于备忘录模式来说,比较贴切的场景应该是游戏中的存档功能,该功能就是将游戏进度存储到

备忘录模式

备忘录模式,望文生义就知道它是用来做备忘的,或者可以直接说是“备份”.当需要保存当前状态,以便在不久要恢复此状态时,就可以使用“备忘录模式”.将当前”状态“备份,是不是又new一个类,然后将每个字段方法copy过去就可以了呢?或者说使用我们之前clone方法做深复制浅复制呢?其实不然,在<大话设计模式>中,作者提到了原因,这样会暴露更多的细节给客户端,不符合我们面向对象的思想.什么是暴露更多的细节给客户端?我们来看下面一段代码. 1 package day_27_memento; 2 3 /*

设计模式(12)---备忘录模式

备忘录模式   Memento (行为型模式) 1.概述 备忘录模式:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态. 例如:一些软件提供的撤销功能,游戏的存档读档功能. 2.结构图 3.代码 1 /* 2 * 原发器类 Originator 3 */ 4 public class Role { 5 6 private int level ; 7 private int life ; 8 9 public Role() { 10