备忘录对象是一个用来存储另外一个对象内部状态的对象。
备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。
该模式有三个角色:发起人角色、备忘录角色、负责人角色
发起人角色:
1 创建一个含有当前的内部状态的备忘录对象
2 使用备忘录对象存储其内部状态
备忘录角色:
1 将发起人角色的状态存储起来
2 备忘录可以选择是否保护其内部保存的发起人角色的状态不被发起人角色之外的角色读取到(黑箱、白箱)
备忘录有两个等价的接口:
窄接口:负责人对象和其他除发起人对象外的任何对象看到的是窄接口,这个窄接口只允许他把备忘录对象传给其他对象;
宽接口:发起人对象可以看到一个宽接口,这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态
负责人角色:
1 负责保存备忘录对象
2 不检查备忘录对象的内容
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
白箱实现:(备忘录角色内部所存储的状态对所有对象公开)
package com.hh.memento_01;
/**
* 备忘录角色
* @author hh
* 2017-10-11
*/
public 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;
}
}
package com.hh.memento_01;
/**
* 负责人角色
* @author hh
* 2017-10-11
*/
public class Caretaker {
private Memento memento;
/**
* 备忘录的取值方法
* @return
*/
public Memento retrieveMemento() {
return this.memento;
}
/**
* 备忘录的赋值方法
* @param memento
*/
public void saveMemento(Memento memento) {
this.memento = memento;
}
}
package com.hh.memento_01;
/**
* 发起人角色
* @author hh
* 2017-10-11
*/
public class Originator {
private String state;
/**
* 工厂模式,返还一个新的备忘录对象
* @return
*/
public Memento createMemenot() {
return new Memento(state);
}
/**
* 将发起人恢复到备忘录对象所记载的状态
* @param memento
*/
public void restorememenot(Memento memento) {
this.state = memento.getState();
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
package com.hh.memento_01;
public class Client {
private static Originator o = new Originator();
private static Caretaker c = new Caretaker();
public static void main(String[] args) {
// 改变负责人对象的状态
o.setState("On");
// 创建备忘录对象,并将发起人对象的状态存储起来
c.saveMemento(o.createMemenot());
// 修改发起人对象的状态
o.setState("Off");
// 恢复发起人对象的状态
o.restorememenot(c.retrieveMemento());
}
}
该实现没有给出两个不同的接口(宽、窄接口)
白箱实现的一个明显好处是比较简单,因此常常用作教学目的。白箱实现的一个明显的缺点是破坏对发起人状态的封装
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
黑箱实现
package com.hh.memento_02;
/**
* 备忘录角色(标识接口)
* @author hh
* 2017-10-12
*/
public interface MementoIF {
}
package com.hh.memento_02;
/**
* 发起人角色
* @author hh
* 2017-10-12
*/
public class Originator {
private String state;
/**
* 工厂模式,返还一个新的备忘录对象
* @return
*/
public MementoIF createMemenot() {
return new Memento(this.state);
}
/**
* 将发起人恢复到备忘录对象所记载的状态
* @param memento
*/
public void restorememenot(MementoIF memento) {
Memento aMemento = (Memento)memento;
this.setState(aMemento.getState());
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
/**
* 内部成员类,备忘录
* @author hh
* 2017-10-12
*/
protected class Memento implements MementoIF {
private String savedState;
/**
* 构造方法
* @param someState
*/
private Memento(String someState) {
savedState = someState;
}
private String getState() {
return savedState;
}
private void setState(String savedState) {
this.savedState = savedState;
}
}
}
package com.hh.memento_02;
/**
* 负责人角色
* @author hh
* 2017-10-12
*/
public class Caretaker {
private MementoIF memento;
/**
* 备忘录的取值方法
* @return
*/
public MementoIF retrieveMemento() {
return this.memento;
}
/**
* 备忘录的赋值方法
* @param memento
*/
public void saveMemento(MementoIF memento) {
this.memento = memento;
}
}
package com.hh.memento_02;
public class Client {
private static Originator o = new Originator();
private static Caretaker c = new Caretaker();
public static void main(String[] args) {
// 改变负责人对象的状态
o.setState("On");
// 创建备忘录对象,并将发起人对象的状态存储起来
c.saveMemento(o.createMemenot());
// 修改发起人对象的状态
o.setState("Off");
// 恢复发起人对象的状态
o.restorememenot(c.retrieveMemento());
}
}
该实现 Originator类 有一个内部类 Memmento 。这个 Memmento类 实现了 MementoIF接口。由于 Memmento类 是仅对 Originator类 才存在的,多有的其他对象都可以得到
Memmento类 的实例,但只能得到它的 MementoIF接口, 这就巧妙的实现了双重接口。
这两个例子都只能存储一个状态点,或者叫一个检查点(Check Point)。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
多检查点白箱实现:
package com.hh.memento_03;
import java.util.Vector;
/**
* 备忘录角色
* @author hh
* 2017-10-12
*/
public class Memento {
private Vector states;
private int index;
public Memento(Vector states, int index) {
this.states = (Vector)states.clone(); // 复制
this.index = index;
}
public Vector getStates() {
return states;
}
public int getIndex() {
return index;
}
}
package com.hh.memento_03;
import java.util.Enumeration;
import java.util.Vector;
import com.hh.memento_03.Memento;
/**
* 发起人角色
* @author hh
* 2017-10-12
*/
public class Originator {
private Vector states;
private int index;
public Originator() {
states = new Vector();
index = 0;
}
/**
* 工厂方法
* @return 新的备忘录对象
*/
public Memento createMemento() {
return new Memento(states, index);
}
public void restoreMemento(Memento memento) {
states = memento.getStates();
index = memento.getIndex();
}
public void setState(String state) {
this.states.addElement(state);
index++;
}
/**
* 辅助方法,打印出所有的状态
*/
public void printStates() {
System.out.println("Total number of states : " + index);
for (Enumeration e = states.elements(); e.hasMoreElements(); ) {
System.out.println(e.nextElement());
}
}
}
package com.hh.memento_03;
import java.util.Vector;
/**
* 负责人角色
* @author hh
* 2017-10-12
*/
public class Caretaker {
private Originator o;
private Vector mementos = new Vector();
private int current;
public Caretaker(Originator o) {
this.o = o;
current = 0;
}
/**
* 创建一个新的检查点
* @return
*/
public int createMemento() {
Memento memento = o.createMemento();
mementos.addElement(memento);
return current++;
}
/**
* 将发起人恢复到某个检查点
* @param index
*/
public void restoreMemento(int index) {
Memento memento = (Memento) mementos.elementAt(index);
o.restoreMemento(memento);
}
/**
* 将某个检查点删除
* @param index
*/
public void removeMemento(int index) {
mementos.removeElementAt(index);
}
}
package com.hh.memento_03;
public class Client {
private static Originator o = new Originator();
private static Caretaker c = new Caretaker(o);
public static void main(String[] args) {
// 改变状态
o.setState("state 0");
// 建立一个检查点
c.createMemento();
// 改变状态
o.setState("state 1");
// 建立一个检查点
c.createMemento();
// 改变状态
o.setState("state 2");
// 建立一个检查点
c.createMemento();
// 改变状态
o.setState("state 3");
// 建立一个检查点
c.createMemento();
// 改变状态
o.setState("state 4");
// 建立一个检查点
c.createMemento();
// 打印出所有的检查点
o.printStates();
// 恢复到第2个检查点
System.out.println("Restoring to 2");
c.restoreMemento(2);
o.printStates();
// 恢复到第0个检查点
System.out.println("Restoring to 0");
c.restoreMemento(0);
o.printStates();
// 恢复到第3个检查点
System.out.println("Restoring to 3");
c.restoreMemento(3);
o.printStates();
}
}
本段代码需要注意的是 Memento(备忘录角色) 的构造方法里面应该是 states.clone() ,而不应该是直接赋值,如果变为直接复制的话,Mementno的states和外界的states就是指向同一个地址,外界如果改变了后Memento的states也会相应的改变。
本文选自《java与模式》,为本人学习所用