什么是备忘录模式?
备忘录模式是一种对象行为型模式。
备忘录模式是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在未来将对象恢复到原来保存的状态。
备忘录模式相当于对对象的内部状态进行实时记录,以便在未来需要时,可以任意读档。
既然说到读档,那么其实单机游戏中的存档,棋类游戏中的悔棋,其实都是备忘录模式的具体实现。
核心结构
- Originator。原发器,普通类,可以使用它创建一个备忘录,并存储它的内部状态,也可以从这个备忘录中取出它的内部状态并进行恢复,一般我们会将保存内部状态的类设计为原发器。
- Memento。备忘录,存储原发器的内部状态,根据原发器来决定需要保存的内部状态,备忘录的设计一般可以参考原发器的设计,并根据实际需要确定备忘录中的属性,备忘录对象不对外暴露。
- Caretaker。负责人,负责保存备忘录,但是不能对备忘录的内容进行操作或检查。负责人中可以存在多个备忘录,它只负责存储备忘录。
类图展示
代码分析
中国象棋历史悠久,经常对弈,可以提高人的各方面能力。我们在网络上对弈时,没进行一步,计算机都会有记载的,随时方便我们复盘或者悔棋,当然我们是不建议经常悔棋的,毕竟君子一出,驷马难追。
首先我们先设计一个原发器,用它来获取或更新备忘录的状态:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/30.
* 备忘录模式——原发器
*/
public class ChineseChess {
private String piece;
private Integer x;
private Integer y;
public ChineseChess(String piece, Integer x, Integer y) {
this.piece = piece;
this.x = x;
this.y = y;
}
public String getPiece() {
return piece;
}
public Integer getX() {
return x;
}
public Integer getY() {
return y;
}
public Memento move() {
return new Memento(this.piece, this.x, this.y);
}
public void undo(Memento memento) {
this.piece = memento.getPiece();
this.x = memento.getX();
this.y = memento.getY();
}
}
接下来我们就需要声明备忘录,用来存储象棋的每一步:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/30.
* 备忘录模式——备忘录
*/
public class Memento {
private String piece;
private Integer x;
private Integer y;
public Memento(String piece, Integer x, Integer y) {
this.piece = piece;
this.x = x;
this.y = y;
}
public String getPiece() {
return piece;
}
public void setPiece(String piece) {
this.piece = piece;
}
public Integer getX() {
return x;
}
public void setX(Integer x) {
this.x = x;
}
public Integer getY() {
return y;
}
public void setY(Integer y) {
this.y = y;
}
}
最后我们就需要一个负责人,用它来保存备忘录:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/1/30.
* 备忘录模式——负责人
*/
public class Responsibility {
private Stack<Memento> mementoStack = new Stack<Memento>();
public Memento getMemento() {
if (mementoStack.capacity() == 1) {
System.out.println("undo forbidden");
return new Memento("begin", 0, 0);
} else {
mementoStack.pop();
return mementoStack.pop();
}
}
public void setMemento(Memento memento) {
mementoStack.push(memento);
}
}
惯例,测试代码:
/**
* Created by <sunshine> mysunshinedreams@163.com on 2017/6/21.
* 备忘录模式测试
*/
public class MementoTest {
public static void main(String[] args) {
Responsibility responsibility = new Responsibility();
ChineseChess chineseChess = new ChineseChess("炮", 1, 5);
display(chineseChess);
responsibility.setMemento(chineseChess.move());
chineseChess = new ChineseChess("车", 2, 9);
display(chineseChess);
responsibility.setMemento(chineseChess.move());
chineseChess.undo(responsibility.getMemento());
display(chineseChess);
}
private static void display(ChineseChess chineseChess) {
System.out.println(String.format("棋子:%s,第%s行,第%s列", chineseChess.getPiece(), chineseChess.getX(), chineseChess.getY()));
}
}
模式总结
优点
- 提供了状态容器,方便获取或恢复对象的历史状态。
缺点
- 对资源消耗过大,因为在使用过程中,可能需要保存大量的对象状态,这些对象状态短时间内可能无法释放,对堆内存及系统性能造成了一定的影响。
适用场景
- 需要保存对象的历史状态,以便未来对其进行获取或者恢复。
- 历史状态不暴露给外界,保证了对象历史状态的安全性。