备忘录模式(Memento Pattern):不破坏封装的前提下,捕捉一个对象的内部状态,并在这个对象之外存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。
备忘录模式又叫标记模式和快照模式,是对象的行为型模式。
备忘录对象是用来储存在某个时刻一个对象内部状态的对象。
采用备忘录模式,我们可以使用一个备忘录对象来储存某个时刻一个对象的内部状态,当用户错误操作或者取消操作的时候能够恢复到原来的状态。
图片来源于网络
发起人角色(Originator):负责创建一个备忘录对象用来记录当前自身的内部状态,并可以利用一个备忘录对象恢复到原来的状态。发起人可以决定备忘录储存自己的哪些状态。
备忘录角色(Memento):负责储存发起人的内部状态。一般和发起人拥有相同的成员变量。
管理者角色(Caretaker):负责管理备忘录对象,但是不能更改备忘录对象的内部状态。
(1)给用户提供了一种可以恢复状态的机制,可以使用户很方便的还原到某个历史状态。
(2)实现了信息的封装,用户不用关心备忘录对象的保存细节。准守“迪米特法则”。
消耗系统资源。当对象的成员变量很多的时候,每保存一个备忘录对象就会消耗更多的系统资源。
(1)游戏存档。
(2)撤销操作。
(3)类似于操作系统的Ctrl+Z操作。
(4)数据库的事务管理。数据库出现问题可以还原到最近的备份点。
此种方式是最简单的备忘录模式。发起人只有一个内部状态,而且只能备份一个备忘录。
此例子采用“白箱实现”,备忘录对象对任何对象都提供一个宽接口(允许访问所有数据),备忘录对象存储的所有内部状态对所有对象都是公开的,是具有破坏封装性的,这种方式就成为“白箱实现”。
(1)发起人类
/**
* 发起人角色类
* 发起人新建一个备忘录对象将自己的状态储存起来
*/
public 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.state=memento.getState();
}
}
(2)备忘录类
/**
* 备忘录角色
* 将发起人角色传入的状态储存起来
*/
public class Memento {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Memento(String state) {
this.state=state;
}
}
(3)管理者类
/**
* 负责人角色类
* 负责人负责保存备忘录对象
* 但是负责人不修改、查看备忘录对象的内容
* (注意,因为备忘录对象是完全暴露给所有接口的,
* 此处负责人是有能力修改备忘录的状态的。)
*/
public class Caretaker {
private Memento memento;
public Memento retrieveMemento() {
return memento;
}
public void saveMemento(Memento memento) {
this.memento = memento;
//由于备忘录为所有对象提供了宽接口,因此管理者也可以修改备忘录的状态
//memento.setState("CCC");
//System.out.println("负责人修改了备忘录对象状态为CCC");
}
}
(4)测试
public class Client {
public static void main(String[] args) {
//new发起人对象
Originator ori=new Originator();
ori.setState("AAA");
System.out.println("发起人第一次的状态:"+ori.getState());
//new负责人对象
Caretaker caretaker=new Caretaker();
//保存备忘录对象
caretaker.saveMemento(ori.createMemento());
//发起人对象更改状态
ori.setState("BBB");
System.out.println("发起人修改后的状态:"+ori.getState());
//回复发起人之前状态
ori.restoreMemento(caretaker.retrieveMemento());
System.out.println("发起人恢复后的状态:"+ori.getState());
}
}
(5)测试结果
发起人第一次的状态:AAA
发起人修改后的状态:BBB
发起人恢复后的状态:AAA
此示例的发起人具有多个内部状态,此处采用“黑箱实现”。备忘录对象为发起人提供一个宽接口,为管理者或其他对象提供一个窄接口(只允许传递对象,不允许访问数据),这种隔离访问的方式称为java双接口,这种实现方式称为“黑箱实现”。Java语言中实现双接口的方式可以将备忘录对象设置成发起人对象的内部类,然后让备忘录对象实现一个标记接口。
(1)发起人类
/**
* 发起人角色类
*/
public class Originator {
private String state;
private String state2;
public String getState2() {
return state2;
}
public void setState2(String state2) {
this.state2 = state2;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
/**
* 返回一个新的备忘录对象
*/
public IMemento createMemento(){
return new Memento(this.state,this.state2);
}
/**
* 将发起人对象恢复到备忘录储存的状态
*/
public void restoreMemento(IMemento memento){
this.state=((Memento)memento).getState();
this.state2=((Memento)memento).getState2();
}
/**
* 备忘录对象类,是发起人的内部类
* 所有方法和属性都是private私有的,
* 因此只有他自己和发起人对象能调用
*/
private class Memento implements IMemento{
private String state;
private String state2;
public String getState2() {
return state2;
}
public void setState2(String state2) {
this.state2 = state2;
}
private String getState() {
return state;
}
private void setState(String state) {
this.state = state;
}
private Memento(String state,String state2) {
this.state=state;
this.state2=state2;
}
}
}
(2)备忘录接口
/**
* 窄接口IMemento,
* 这是一个标识接口,
* 因此它没有定义出任何的方法。
*/
public interface IMemento {}
(3)管理者类
/**
* 负责人角色类
* 负责人角色类Caretaker通过IMemento接口管理备忘录对象,
* 由于这个接口仅仅是一个标识接口,
* 因此负责人角色不可能改变这个备忘录对象的内容。
*/
public class Caretaker {
private IMemento memento;
public IMemento retrieveMemento() {
return memento;
}
public void saveMemento(IMemento memento) {
this.memento = memento;
}
}
(4)测试
public class Client {
public static void main(String[] args) {
//new发起人对象
Originator ori=new Originator();
ori.setState("AAA");
ori.setState2("1");
System.out.println("发起人第一次的状态:"+ori.getState()+","+ori.getState2());
//new负责人对象
Caretaker caretaker=new Caretaker();
//保存备忘录对象
caretaker.saveMemento(ori.createMemento());
//发起人对象更改状态
ori.setState("BBB");
ori.setState2("2");
System.out.println("发起人修改后的状态:"+ori.getState()+","+ori.getState2());
//回复发起人之前状态
ori.restoreMemento(caretaker.retrieveMemento());
System.out.println("发起人恢复后的状态:"+ori.getState()+","+ori.getState2());
}
}
(5)测试结果
发起人第一次的状态:AAA,1
发起人修改后的状态:BBB,2
发起人恢复后的状态:AAA,1
一般在使用备忘录模式的时候,发起人通常是一个对象,对象中的变量不只一个,需要备份的状态也不只一份,这就是多状态多备份备忘录。此示例也采用“黑箱实现”。
(1)发起人类
/**
* 发起人角色类
*/
public class Originator {
private String state;
private String state2;
public String getState2() {
return state2;
}
public void setState2(String state2) {
this.state2 = state2;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
/**
* 返回一个新的备忘录对象
*/
public IMemento createMemento(){
return new Memento(this.state,this.state2);
}
/**
* 将发起人对象恢复到备忘录储存的状态
*/
public void restoreMemento(IMemento memento){
this.state=((Memento)memento).getState();
this.state2=((Memento)memento).getState2();
}
/**
* 备忘录对象类,是发起人的内部类
* 所有方法和属性都是private私有的,
* 因此只有他自己和发起人对象能调用
*/
private class Memento implements IMemento{
private String state;
private String state2;
public String getState2() {
return state2;
}
public void setState2(String state2) {
this.state2 = state2;
}
private String getState() {
return state;
}
private void setState(String state) {
this.state = state;
}
private Memento(String state,String state2) {
this.state=state;
this.state2=state2;
}
}
}
(2)备忘录接口
/**
* 窄接口IMemento,
* 这是一个标识接口,
* 因此它没有定义出任何的方法。
*/
public interface IMemento {}
(3)管理者类
/**
* 负责人角色类
* 负责人角色类Caretaker通过IMemento接口管理备忘录对象,
* 由于这个接口仅仅是一个标识接口,
* 因此负责人角色不可能改变这个备忘录对象的内容。
*/
public class Caretaker {
//储存多个备忘录对象
private HashMap mementos=new HashMap();
public IMemento retrieveMemento(String key) {
return mementos.get(key);
}
public void saveMemento(String key,IMemento memento) {
this.mementos.put(key, memento);
}
}
(4)测试
public class Client {
public static void main(String[] args) {
// new发起人对象
Originator ori = new Originator();
ori.setState("中国");
ori.setState2("富强");
System.out.println("发起人第1次的状态:" + ori.getState() + "," + ori.getState2());
// new负责人对象
Caretaker caretaker = new Caretaker();
// 保存备忘录对象
caretaker.saveMemento("1", ori.createMemento());
// 发起人对象更改状态
ori.setState("依法治国");
ori.setState2("民主和谐");
System.out.println("发起人第2次的状态:" + ori.getState() + "," + ori.getState2());
caretaker.saveMemento("2", ori.createMemento());
// 发起人对象再次变更状态
ori.setState("软件");
ori.setState2("发达");
System.out.println("发起人第3次的状态:" + ori.getState() + "," + ori.getState2());
caretaker.saveMemento("3", ori.createMemento());
// 恢复发起人第一次状态
ori.restoreMemento(caretaker.retrieveMemento("1"));
System.out.println("发起人恢复第1次后的状态:" + ori.getState() + "," + ori.getState2());
// 恢复发起人第3次状态
ori.restoreMemento(caretaker.retrieveMemento("3"));
System.out.println("发起人恢复第3次后的状态:" + ori.getState() + "," + ori.getState2());
}
}
(5)测试结果
发起人第1次的状态:中国,富强
发起人第2次的状态:依法治国,民主和谐
发起人第3次的状态:软件,发达
发起人恢复第1次后的状态:中国,富强
发起人恢复第3次后的状态:软件,发达
参考文章 http://www.cnblogs.com/java-my-life/archive/2012/06/06/2534942.html
http://www.runoob.com/design-pattern/memento-pattern.html
http://alaric.iteye.com/blog/1931253
【四川乐山程序员联盟,欢迎大家加群相互交流学习5 7 1 8 1 4 7 4 3】