设计模式-10 - Memento Design Pattern
1.定义
备忘录模式是一种设计模式,它允许在不破坏封装性的情况下捕获和恢复对象的内部状态。
其结构:
Originator:创建和管理备忘录的对象。
Memento:存储 Originator 状态的备忘录对象。Memento 对象是不可变的。
Caretaker:存储 Memento 对象,而不了解其实际内容。
示例:
一个文本编辑器可以使用备忘录模式来存储文档的状态,以便用户可以撤消和重做编辑操作。Originator 是文档对象,Memento 是存储文档状态的文本快照,Caretaker 是管理备忘录的撤消/重做管理器。
bash
+--------------+
| Originator |
+--------------+
|
v
+--------------+
| Memento |
+--------------+
|
v
+--------------+
| Caretaker |
+--------------+
|
v
(stores Memento)
调用关系:
- Originator 创建一个 Memento 对象来存储其当前状态。
- Caretaker 从 Originator 接收 Memento 对象并将其存储起来。
- Originator 修改其状态。
- Originator 从 Caretaker 恢复其状态,传递先前创建的 Memento 对象。
- Caretaker 将 Memento 对象传递给 Originator。
- Originator 使用 Memento 对象恢复其状态。
说明:
Originator:创建和修改其状态,并创建 Memento 对象来存储其状态。
Memento:存储 Originator 的状态,以便以后可以恢复。
Caretaker:负责存储和管理 Memento 对象。
2.内涵
工作原理:
Originator 创建一个 Memento 对象,该对象捕获其当前状态。
Originator 可以将 Memento 对象传递给 Caretaker。
当 Originator 需要恢复其状态时,它可以从 Caretaker 请求 Memento 对象。
Originator 使用 Memento 对象恢复其内部状态。
3.使用示例
cpp
#include <iostream>
#include <string>
#include <vector>
// Originator: The object whose state needs to be saved and restored.
class Originator {
private:
std::string state;
public:
void SetState(const std::string& newState) {
state = newState;
}
std::string GetState() const {
return state;
}
// Memento: Inner class representing the state of the Originator.
class Memento {
private:
std::string state;
public:
Memento(const std::string& originatorState) : state(originatorState) {}
std::string GetSavedState() const {
return state;
}
};
// Create a Memento object to save the current state.
Memento CreateMemento() const {
return Memento(state);
}
// Restore the state from a Memento object.
void RestoreState(const Memento& memento) {
state = memento.GetSavedState();
}
};
// Caretaker: Manages the Memento objects.
class Caretaker {
private:
std::vector<Originator::Memento> mementos;
public:
void AddMemento(const Originator::Memento& memento) {
mementos.push_back(memento);
}
Originator::Memento GetMemento(int index) const {
if (index >= 0 && index < mementos.size()) {
return mementos[index];
}
throw std::out_of_range("Invalid Memento index");
}
};
int main() {
Originator originator;
Caretaker caretaker;
originator.SetState("State 1");
caretaker.AddMemento(originator.CreateMemento());
originator.SetState("State 2");
caretaker.AddMemento(originator.CreateMemento());
// Restore to the previous state
originator.RestoreState(caretaker.GetMemento(0));
std::cout << "Current state: " << originator.GetState() << std::endl;
// Restore to an even earlier state
originator.RestoreState(caretaker.GetMemento(1));
std::cout << "Current state: " << originator.GetState() << std::endl;
return 0;
}
4.注意事项
备忘录模式需要注意的事项:
- 只存储相关状态:Memento 对象应仅存储 Originator 的相关状态,而不是其所有状态。这有助于保持 Memento 的大小较小并提高性能。
- 避免存储引用:Memento 对象不应存储对其他对象的引用,因为这可能会导致循环引用和内存泄漏。
- 考虑序列化:如果需要在进程或计算机之间传输 Memento 对象,则需要考虑序列化和反序列化机制。
- 版本控制:如果 Originator 的状态可能会随着时间的推移而改变,则需要考虑使用版本控制机制来管理 Memento 对象的不同版本。
- 并发访问:在并发环境中使用备忘录模式时,需要确保 Originator、Memento 和 Caretaker 类都是线程安全的,并且并发访问备忘录是原子的。
- 性能影响:频繁创建和存储 Memento 对象可能会对应用程序的性能产生影响,尤其是在处理大型或复杂的状态时。
- 设计复杂性:备忘录模式可能会增加应用程序的设计复杂性,尤其是在需要管理多个 Originator 和 Memento 对象时
5.最佳实践
当需要以下场景时,需要使用备忘录设计模式:
- 当需要在不破坏封装性的情况下捕获和恢复对象的内部状态时。
- 当需要多次撤消和重做操作时。
- 当需要将复杂的对象图存储为快照时。
6.总结
在设计一个需要频繁保存状态的应用,如何确保备忘录模式在并发环境中有效应对并发访问,这里有些方法可以借鉴:
- 线程安全:确保 Originator、Memento 和 Caretaker 类都是线程安全的。这可以采用多种方法实现,例如使用同步机制(例如锁或互斥量)或使用不可变对象。
- 只读备忘录:使 Memento 对象不可变。这将防止并发访问时意外修改备忘录的内容。
- 并发控制:在 Caretaker 中使用适当的并发控制机制,例如读写锁或原子操作,以确保对备忘录的访问是原子的。
- 隔离备忘录:为每个 Originator 实例维护一个单独的备忘录存储。这将防止不同 Originator 实例之间的并发访问冲突。
- 使用版本控制:为备忘录引入版本控制机制。这将允许跟踪和恢复备忘录的不同版本,即使在并发访问的情况下也是如此。
其他考虑因素:
- 性能:在高并发环境中,确保备忘录模式的实现不会引入明显的性能开销。
- 可扩展性:备忘录模式的实现应该具有可扩展性,以便在需要时可以轻松扩展以支持更多的并发访问。
- 测试:彻底测试备忘录模式的实现,以确保其在并发环境中按预期工作。
通过遵循这些准则,你可以确保备忘录模式在需要频繁保存状态的并发应用程序中能有效应对并发访问。