ealenxie

备忘录模式 : Memento

声明/作用 : 保存对象的内部状态,并在需要的时候(undo/rollback) 恢复到对象以前的状态

适用场景 : 一个对象需要保存状态,并且可通过undo或者rollback恢复到以前的状态时,可以使用备忘录模式

经典场景 : 某时刻游戏存档恢复记录

需要被保存内部状态以便恢复的这个类 叫做 : Originator 发起人(原生者)

用来保存Originator内部状态的类 叫做 : Memento 备忘录(回忆者) 它由Originator创建

负责管理备忘录Memento的类叫做 : Caretaker 看管者(管理者),它不能对Memento的内容进行访问或者操作。

以Person对象(拥有name,sex,age三个基本属性)为例 : 

package name.ealen.memento.noDesignPattern;

/**
 * Created by EalenXie on 2018/9/27 15:18.
 */
public class Person {

    private String name;
    private String sex;
    private Integer age;

    public Person(String name, String sex, Integer age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    //省略getter,setter
}

 

如果不使用设计模式,我们要对其进行备份,undo操作 ,常规情况下,我们可能会写出如下的代码 : 

 

 /**
     * 不使用设计模式 实现备忘录模式,普通保存实例的内部状态
     */
    @Test
    public void noDesignPattern() {
        Person person = new Person("ealenxie", "男", 23);

        //1 . 首先新建一个Person的Backup备份,将对象的初始属性赋值进去
        Person backup = new Person();
        backup.setName(person.getName());
        backup.setSex(person.getSex());
        backup.setAge(person.getAge());
        //打印初始的person
        System.out.println("初始化的对象 : " + person);

        //2 . 修改person
        person.setAge(22);
        person.setName("ZHANG SAN");
        person.setSex("女");
        System.out.println("修改后的对象 : " + person);

        //3 . 回滚(回复以前状态) 从backup中获取之前的状态,重新赋值
        person.setAge(backup.getAge());
        person.setName(backup.getName());
        person.setSex(backup.getSex());
        System.out.println("还原后的对象 : " + person);
    }

运行可以看到基本效果 :  

    

以上代码中,我们首先进行了创建了一个初始对象person,然后new出一个新的backup,将初始对象的属性赋给backup,person修改之后,如果进行undo/rollback,就将backup的属性重新赋值给对象person。这样做我们必须要关注person和backup之间的赋值关系必须一致且值正确,这样才能完成rollback动作;如果person对象拥有诸多属性及行为的话,很显示不是特别的合理。

 

如下,我们使用备忘录模式来完成对象的备份和rollback

  1 . 首先,我们定义Memento对象,它的作用就是用来保存 初始对象(原生者,此例比如person)的内部状态,因此它的属性和原生者应该一致。

package name.ealen.memento.designPattern;

/**
 * Created by EalenXie on 2018/9/27 18:03.
 */
public class Memento {
    private String name;
    private String sex;
    private Integer age;
    public Memento(String name, String sex, Integer age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    //省略getter/setter
}

 

  2 . 然后,定义我们的发起人(原生者) Originator,它拥有两个基本的行为 : 

    1). 创建备份

    2). 根据备份进行rollback

package name.ealen.memento.designPattern;

/**
 * Created by EalenXie on 2018/9/27 18:02.
 */
public class Originator {

    private String name;
    private String sex;
    private Integer age;

    //创建一个备份
    public Memento createMemento() {
        return new Memento(name, sex, age);
    }

    //根据备份进行rollback
    public void rollbackByMemento(Memento memento) {
        this.name = memento.getName();
        this.sex = memento.getSex();
        this.age = memento.getAge();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Originator(String name, String sex, Integer age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Originator{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }
}

 

  3 . 为了防止发起者与备份对象的过度耦合,以及防止对发起者行和属性进行过多的代码侵入,我们通常将Memento对象交由CareTaker来进行管理 : 

 

package name.ealen.memento.designPattern;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by EalenXie on 2018/9/27 17:39.
 */
public class CareTaker {

    private Map<String, Memento> mementos = new HashMap<>(); //一个或者多个备份录

    //保存默认备份
    public void saveDefaultMemento(Memento memento) {
        mementos.put("default", memento);
    }

    //获取默认备份
    public Memento getMementoByDefault() {
        return mementos.get("default");
    }

    //根据备份名 保存备份
    public void saveMementoByName(String mementoName, Memento memento) {
        mementos.put(mementoName, memento);
    }

    //根据备份名 获取备份
    public Memento getMementoByName(String mementoName) {
        return mementos.get(mementoName);
    }

    //删除默认备份
    public void deleteDefaultMemento() {
        mementos.remove("default");
    }

    //根据备份名 删除备份
    public void deleteMementoByName(String mementoName) {
        mementos.remove(mementoName);
    }
}

 

  4 . 此时,我们要进行备份以及rollback,做法如下 : 

 

    /**
     * 备忘录模式,标准实现
     */
    @Test
    public void designPattern() {
        Originator originator = new Originator("ealenxie", "男", 22);
        CareTaker careTaker = new CareTaker();

        //新建一个默认备份,将Originator的初始属性赋值进去
        careTaker.saveDefaultMemento(originator.createMemento());

        //初始化的Originator
        System.out.println("初始化的对象 : " + originator);

        //修改后的Originator
        originator.setName("ZHANG SAN");
        originator.setSex("女");
        originator.setAge(23);
        System.out.println("第一次修改后的对象 : " + originator);

        //新建一个修改后的备份
        careTaker.saveMementoByName("第一次修改备份", originator.createMemento());

        //根据默认备份还原rollback后的Originator
        originator.rollbackByMemento(careTaker.getMementoByDefault());
        System.out.println("还原后的对象 : " + originator);

        //根据备份名还原rollback后的Originator
        originator.rollbackByMemento(careTaker.getMementoByName("第一次修改备份"));
        System.out.println("第一次修改备份 : " + originator);

        //再创建一个默认备份
        careTaker.saveDefaultMemento(originator.createMemento());
        originator.rollbackByMemento(careTaker.getMementoByDefault());
        System.out.println("最后创建的默认备份 : " + originator);
    }

 

运行可以看到如下结果 : 

  

 以上,使用备忘录的设计模式,好处是显而易见的,我们应该关注的是对象本身具有的(备份/rollback)的行为,而非对象之间的赋值。

 

相关文章: