针对问题
现有一个可变的类,其内部状态不断变化,我们想要捕捉其中的变化,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。这种情况下可以用备忘录模式。
如何实现
创建两个类并在原先的可变类中添加一些方法即可。首先在状态的产生类定义两个方法,可以产生状态和复原状态;然后创建一个备忘录类作为状态存储的单元结构;之后再创建一个类用来管理所有已有的状态,即保存于很多个备忘录类的实例。客户不与备忘录类耦合,与备忘录管理类耦合。
优点
1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
2、实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点
消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
例子
1、后悔药。
2、打游戏时的存档。
3、Windows 里的 ctri + z。
4、IE 中的后退。
5、数据库的事务管理。
类图
实例展示
这里我们给出一个原子轨道系统中电子跃迁的例子,这里的状态就是电子的分布情况。首先我们要能够保存和复原状态,即在原始类里面添加方法,如下:
package circularOrbit;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import centralObject.Nucleus;
import memento.Memento;
import physicalObject.Electron;
import track.Track;
/**
* A Person
* immutable.
*
*/
public class AtomStructure extends ConcreteCircularOrbit<Nucleus, Electron> {
private final static String type = "AtomStructure";
private String state = "";
private final Map<Integer, Integer> eletronnumbrestate = new HashMap<Integer, Integer>();
// Abstraction function:
// type represent the type of ConcreteCircularOrbit.
// state represent electron distribution information in orbit.
// eletronnumbrestate represent electron distribution in orbit.
// Representation invariant:
// The state must be in the format of "1/2;2/4;3/15".
// The eletronnumbrestate must match the state.
// Safety from rep exposure:
// No observer.
/*
*......
*中间删去了其他方法
*......
*/
/**
* Reset the distribution of electrons according to the given state.
* @param the target state.
*/
public void setState(String state) {
this.state = state;
Nucleus cenNucleus = getcentralObject();
this.empty();
String[] infos = state.split(";");
for (int i = 0; i < infos.length; i++) {
String string = infos[i];
int index2 = string.indexOf('/');
String first = string.substring(0, index2);
int firstnum = Integer.valueOf(first);
String second = string.substring(index2 + 1);
int secondnum = Integer.valueOf(second);
eletronnumbrestate.put(firstnum, secondnum);
}
this.addCentralObject(cenNucleus);
for (int i = 1; i <= eletronnumbrestate.size(); i++) {
int electronnum = eletronnumbrestate.get(i);
for (int j = 1; j <= electronnum; j++) {
Electron newelec = new Electron(i + "-" + j);
this.addAPhysicalObject(i, newelec);
}
}
}
/**
* Gets the state of the current electron distribution.
*
*
*
* @return the current state.
*/
public String getState() {
String currentstate = "";
for (int i = 0; i < getTracks().size(); i++) {
Track tempTrack = getTracks().get(i);
int size = getPhysicalObjectOfTrack(tempTrack).size();
currentstate = currentstate + tempTrack.getRadius() + '/' + size + ";";
eletronnumbrestate.put(tempTrack.getRadius(), size);
}
if (currentstate.length() == 0) {
return "";
}
currentstate = currentstate.substring(0, currentstate.length()-1);
state = currentstate;
return state;
}
/**
* Save the state to a new memoto.
*
*
*
* @return Memoto generated from the current state.
*/
public Memento saveStateToMemento() {
return new Memento(getState());
}
/**
* Reset the distribution of electrons according to the given memento.
*
*
* @param Memoto for storage state.
*/
public void getStateFromMemento(Memento Memento) {
state = Memento.getState();
setState(state);
}
}
可以发现我们这里实际上给出了四个方法,其中前面两个是为了内部获取和复原状态,后面两个才是实际上和备忘录交互用的。
之后我们需要创建备忘录类,作为存储状态的单元,如下:
package memento;
/**
* A memento.
* immutable.
*
*/
public class Memento {
private final String state;
// Abstraction function:
// The state represent the state stored.
// Representation invariant:
// Nothing.
// Safety from rep exposure:
// state is immutable.
/**
* Create a new memento.
*
*/
public Memento(String state) {
this.state = state;
}
/**
* Get the state.
*
* @return the state.
*/
public String getState() {
return state;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((state == null) ? 0 : state.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Memento other = (Memento) obj;
if (state == null) {
if (other.state != null)
return false;
} else if (!state.equals(other.state))
return false;
return true;
}
@Override
public String toString() {
return "Memento [state=" + state + "]";
}
}
最后我们创建一个备忘录的管理类用于和客户端进行交互即可,如下:
package memento;
import java.util.ArrayList;
import java.util.List;
/**
* A CareTaker.
* The caretaker is responsible for the storage of all memotos.
* mutable.
*
*/
public class CareTaker {
private final List<Memento> mementoList = new ArrayList<Memento>();
// Abstraction function:
// mmementoList represent all mementos.
// Representation invariant:
// Nothing.
// Safety from rep exposure:
// mementoList is private and final.
/**
* Add a memento.
*
*
* @param the memento need to be added.
*/
public void add(Memento state) {
mementoList.add(state);
}
/**
* Get a memento.
*
*
* @param the index of the memento in the mmementoList.
* @return the memento.
*/
public Memento get(int index) {
if (index >= mementoList.size()) {
return null;
}
return mementoList.get(index);
}
/**
* Show all state information through a string.
*
*
*
*/
public void showAllStates() {
System.out.println("All States:");
for (int i = 0; i < mementoList.size(); i++) {
Memento tempMemento = mementoList.get(i);
System.out.println("\tState" + (i+1) + ":" + tempMemento.getState());
}
}
@Override
public String toString() {
// TODO 自动生成的方法存根
return mementoList.toString();
}
}