备忘录模式 : 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)的行为,而非对象之间的赋值。