备忘录模式:拍下照片
摘要生成中...
AI 摘要
Hunyuan-lite

备忘录模式/快照模式 Memento Design Pattern
Captures and externalizes an object’s internal state so that it can be restored later, all without violating encapsulation.——GoF
在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。

备忘录模式可以实现的功能:
- Undo(撤销)
- Redo(重做)
- History(历史记录)
- Snapshot(快照)
备忘录模式的实现很灵活,也没有很固定的实现方式,在不同的业务需求、不同编程语言下,代码实现可能都不大一样。

注意:
Originator和Memento不是父子关系。Caretaker无法new出Memento实例,只能通知Originator去new。- 为了实现访问权限控制,注意
Originator和Memento的包的关系。
1 | class Originator{ |
登场角色:
Originator(原发器):Originator角色会在保存自己的最新状态时生成Memento角色。当把以前保存的Memento角色传递给Originator角色时,它会将自己恢复至生成该Memento角色时的状态。Memento(备忘录):Originator角色和Memento角色之间有着非常紧密的联系。Memento角色会将Originator角色的内部信息整合在一起。- 在
Memento角色中虽然保存了Originator角色的信息,但它不会向外部公开这些信息。Memento角色有以下两种接口(API)以防止对象的封装性被破坏:- 宽接口(API)——Wide interface。
Memento角色提供的「宽接口(API)」是指所有用于获取恢复对象状态信息的方法的集合。由于宽接口(API)会暴露所有Memento角色的内部信息,因此能够使用宽接口(API)的只有Originator角色。 - 窄接口(API)——Narrow interface。
Memento角色为外部的Caretaker角色提供了「窄接口(API)」。可以通过窄接口(API)获取的Memento角色的内部信息非常有限,因此可以有效地防止信息泄露。
- 宽接口(API)——Wide interface。
Caretaker(负责人):当Caretaker角色想要保存当前的Originator角色的状态时,会通知Originator角色。Originator角色在接收到通知后会生成Memento角色的实例并将其返回给Caretaker角色。- 负责人仅知道「何时」和「为何」捕捉原发器的状态,以及何时恢复状态。
- 由于以后可能会用
Memento实例来将Originator恢复至原来的状态,因此Caretaker角色会一直保存Memento实例。不过,Caretaker角色只能使用Memento角色的窄接口(API),也就是说它无法访问Memento角色内部的所有信息。它只是将Originator角色生成的Memento角色当作一个黑盒子保存起来。 - 虽然
Originator角色和Memento角色之间是强关联关系,但Caretaker角色和Memento角色之间是弱关联关系。Memento角色对Caretaker角色隐藏了自身的内部信息。
适用于不支持嵌套类的编程语言的实现方法
比如 PHP 语言。

- 在没有嵌套类的情况下, 你可以规定负责人仅可通过明确声明的中间接口与备忘录互动, 该接口仅声明与备忘录元数据相关的方法, 限制其对备忘录成员变量的直接访问权限。
- 另一方面, 原发器可以直接与备忘录对象进行交互, 访问备忘录类中声明的成员变量和方法。 这种方式的缺点在于你需要将备忘录的所有成员变量声明为公有。
封装更加严格的实现

- 这种实现方式允许存在多种不同类型的原发器和备忘录。 每种原发器都和其相应的备忘录类进行交互。 原发器和备忘录都不会将其状态暴露给其他类。
- 负责人此时被明确禁止修改存储在备忘录中的状态。 但负责人类将独立于原发器, 因为此时恢复方法被定义在了备忘录类中。
- 每个备忘录将与创建了自身的原发器连接。 原发器会将自己及状态传递给备忘录的构造函数。 由于这些类之间的紧密联系, 只要原发器定义了合适的设置器 (setter), 备忘录就能恢复其状态。
拓展思路:
- 两种接口(API)的可见性。控制访问权限。
- 可以实现保存多个
Memento,这样就可以保存各个时间点的状态。 - 注意
Memento的有效期。 Caretaker决定何时拍照,何时撤销以及保存Memento角色。Originator角色的职责是生成Memento角色和使用接收到的Memento角色来恢复自己的状态。这样的职责分担,当遇到以下需求时,Originator不用改变:- 可以多次撤销
- 不仅可以多次撤销,还可以将现在的状态保存在文件中
| 优点 | 缺点 |
|---|---|
| 可以在不破坏对象封装情况的前提下创建对象状态快照。 | 如果客户端过于频繁地创建备忘录,程序将消耗大量内存。 |
| 可以通过让负责人维护原发器状态历史记录来简化原发器代码。 | 负责人必须完整跟踪原发器的生命周期,这样才能销毁弃用的备忘录。 |
| 绝大部分动态编程语言(例如 PHP、Python 和 JavaScript)不能确保备忘录中的状态不被修改。 |
备忘录模式与「备份」的概念区别:
- 这两者的应用场景很类似,都应用在防丢失、恢复、撤销等场景中。
- 备忘录模式更侧重于代码的设计和实现;备份更侧重架构设计或产品设计。
使用「低频率全量备份」和「高频率增量备份」方式可以优化内存和时间消耗。

相关的设计模式:
- 站内文章命令模式:在使用命令模式处理命令时,可以使用备忘录模式实现撤销功能。
- 站内文章原型模式:在备忘录模式中,为了能够实现快照和撤销功能,保存了对象当前的状态。保存的信息只是在恢复状态时所需要的那部分信息。而在原型模式中,会生成一个与当前实例完全相同的另外一个实例。这两个实例的内容完全一样。
- 站内文章状态模式:在备忘录模式中,是用「实例」表示状态。而在状态模式中,则是用「类」表示状态。
- 站内文章迭代器模式:可以同时使用备忘录和迭代器模式来获取当前迭代器的状态,并且在需要的时候进行回滚。
本文参考
- 《图解设计模式》第 18 章
- 本科生课程笔记《程序设计中级实践&设计模式》 - TJU 🍐⚱️
- 极客时间专栏 - 设计模式之美 - 王争
- 备忘录设计模式
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 半方池水半方田!
评论





