基本概念
Memento Pattern: Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.
相关联系
- 可以同时使用 命令模式 和备忘录模式来实现 “撤销”
- 命令模式通过执行命令的逆操作,更关注操作的封装和管理。
- 备忘录关注于直接保存对象的状态
- 可以同时使用 迭代器模式 和备忘录模式来获取当前迭代器的状态, 在要时进行回滚。
- 有时候 原型模式 可以作为备忘录模式的一个简化版本, 其条件是需要在历史记录中存储的对象的状态比较简单, 不需要链接其他外部资源, 或者链接可以方便地重建。
实现代码
备忘录类通常被嵌套在原发器中。 这样原发器就即可以访问备忘录的成员变量和方法, 负责人对于备忘录的成员变量和方法的访问权限同时非常有限: 它们只能在栈中保存备忘录, 而不能修改其状态。
- 这种实现方式依赖于许多编程语言 (例如 C++、 C# 和 Java) 所支持的嵌套类。
- PHP 等不支持嵌套类的编程语言 ,为了不让其他类有任何机会通过备忘录来访问原发器的状态,需要保证不给备忘录提供 set 方法(详见 Q&A - 1)。
原发器 & 备忘录
- Originator 类可以生成自身状态的快照, 也可以在需要时通过快照恢复自身状态。
- Memento 类是原发器状态快照的值对象(value object)。 通常做法是将备忘录设为不可变的, 并通过构造函数一次性传递数据。
负责人
Caretaker 类仅知道 “何时” 和 “为何” 捕捉原发器的状态, 以及何时恢复状态。
负责人通过保存备忘录栈来记录原发器的历史状态。 当原发器需要回溯历史状态时, 负责人将从栈中获取最顶部的备忘录, 并将其传递给原发器的恢复 (restoration) 方法。
测试类
Q & A
What are the key advantages of using a memento design pattern?
- The biggest advantage is that you can always discard the unwanted changes and restore it to an intended or stable state.
- Maintains high cohesion.
In many applications, I notice that the memento class is presented as an inner class of Originator. Why are you not following that approach?
- The memento design pattern can be implemented in many different ways
- for example, using package-private visibility or using object serialization techniques.
- But in each case, if you analyze the key aim, you find that once the memento instance is created by an originator, no one else besides its creator is allowed to access the internal state (this includes the caretaker/client).
- A caretaker’s job is to store the memento instance and supply them back when you are in need. So, there is no harm if your memento class is public. You can just block the public setter method for the memento. I believe that it is sufficient enough.
But you are still using the getter method getStateId(). Does it not violate the encapsulation?
There is a lot of discussion and debate around this area—whether you should use getter/setter or not, particularly when you consider encapsulation. I believe that it depends on the amount of strictness that you want to impose.
You do not compromise the encapsulation associated with the key objects that are participating in this model.
To support undo operations, which pattern should I prefer—memento or command?
It depends on the complexity of the operation and whether you need to store the previous state of objects.
- The Memento pattern is preferable when you need to revert to a previous state
- i.e., 50 + 10 = 60, so to go back, you do 60 –10 = 50
- The Command pattern is suitable for situations where undoing an operation is as simple as performing the inverse action.
具体应用
java.io.Serializable
Serializable
接口使对象可以被序列化和反序列化。- 序列化是将对象的状态转换为字节流的过程,以便可以将其写入到存储介质(例如磁盘)或通过网络传输。
- 反序列化是将这个字节流重新构造成原来的对象。