介绍
备忘录应用场景明确并且有限,一般用来数据的防丢失、撤销和恢复。对大对象的备份和恢复,备忘录模式能有效的节省时间和空间开销。
定义
备忘录模式:也称为快照模式,在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态以便后续能将这个对象恢复成之前的状态。
备忘录模式的两个要点
- 存储副本以便后期恢复
- 要在不违反封装原则的前提下恢复
备忘录模式中的角色
- 原发器(Originator):负责创建一个备忘录,用以存储其当前状态;同时也可以利用备忘录恢复其内部状态
- 备忘录(Memento):用于存储原发器对象的内部状态。为了保护原发器对象的封装性,备忘录对象通常被设计为不可直接访问其内容,只暴露给原发器。
- 负责人(Caretaker):负责保管备忘录对象,但不直接读取或修改备忘录的内容。它只是简单的提供一个存储空间,用来在需要的时候传递给原发器。
使用记事本程序来理解各个角色
- 记事本本身为原发器,可以创建备份、从备份恢复
- 记事本备份为 Memento 备忘录,由原发器创建
- 保存所有记事本备份的对象则为负责人,保存所有的记事本备份。
代码示例
未使用备忘录模式实现输入备份
/**
* 简单的文本编辑器
*
* @author Jean
* @date 2024/06/07
*/
class InputText {
private StringBuilder text = new StringBuilder();
public String getText() {
return text.toString();
}
public void append(String input) {
text.append(input);
}
public void setText(String text) {
this.text.replace(0, this.text.length(), text);
}
}
/**
* 保存并处理快照
*
* @author Jean
* @date 2024/06/07
*/
public class SnapshotHolder {
private Stack<InputText> snapshots = new Stack<>();
public InputText popSnapshot() {
return snapshots.pop();
}
public void pushSnapshot(InputText inputText) {
InputText deepClonedInputText = new InputText();
deepClonedInputText.setText(inputText.getText());
snapshots.push(deepClonedInputText);
}
}
问题:
- 为了能尽快恢复快照使用了 setText ,可能导致被其他业务误用,方法违背了封装原则。
- 快照本身应该是不可变的,不应该包含任何修改内部状态的函数,但是上述实现直接复用 InputText 的定义,也违反了快照的封装原则。
使用备忘录模式实现输入备份
//定义一个文本编辑器,能保存编辑的文本
//原发器(Originator) 负责创建一个备忘录,用以存储其当前状态;同时也可以利用备忘录恢复其内部状态。
class InputText{
private StringBuilder text = new StringBuilder();
public String getText(){
return text.toString();
}
public void append(String input){
text.append(input);
}
}
//备忘录角色
//用于存储原发器对象的内部状态。为了保护原发器对象的封装性,备忘录对象通常被设计为不可直接访问其内容,只暴露给原发器。
class SnapShot{
private String text;
public SnapShot(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
//负责人 caretaker
//负责保管备忘录对象,但不直接读取或修改备忘录的内容。它只是简单的提供一个存储空间,用来在需要的时候传递给原发器。
public class SnapshotHolder{
private Stack<SnapShot> snapShots=new Stack<>();
public SnapShot popSnapshot(){
return snapShots.pop();
}
public void PushSnapshot(SnapShot snapShot){
snapShots.push(snapShot);
}
}
总结
备忘录模式适合的场景如下:
- 当需要实现撤销/重做功能时。
- 当需要保存和恢复对象的内部状态,但又不想暴露这些状态的细节时。
- 在某些性能非关键路径上,可以接受因保存状态而带来的资源消耗。
备忘录模式在实现时,设计者需要权衡状态保存的频率和成本,以确保不会因为过度使用而导致资源紧张。此外,也可以考虑对备忘录进行优化,比如限制保存的状态数量,或者采用更为高效的序列化技术来减少存储开销。