《设计模式》— 行为型模式 — 备忘录模式

一、动机

有时有必要记录一个对象的内部状态。为了允许用户取消不确定的操作或从错误中恢复过来,需要实现检查点和取消机制。而要实现这些机制,你必须实现将状态信息保存在某处,这样才能将对象恢复到它们先前的状态。但是对象通常封装了其部分或所有的状态信息,使得其状态不能被其他对象访问,也就不可能在该对象之外保存其状态。而暴露内部信息状态又将违反封装的原则,可能有损应用的可靠性和可扩展性。

例如,考虑一个图形编辑器,它支持图形对象间的连线。用户可用一条直线连接两个矩形,而当用户移动任意一个矩形时,这两个矩形仍能保持连接。在移动过程中,编辑器自动伸展这条直线以保持该连接。
在这里插入图片描述
一个众所周知的保持对象间连接关系的方式是使用约束解释系统。我们可以将这一功能封装在一个 ConstraintServer 对象中。ConstraintServer 在连接生成时,记录这些连接并产生描述它们的数学方程。当用户生成一个连接或修改图形时,ConstraintServer 就求解这些方程。并根据它的计算结果重新调整图形,是各个对象保持正确的连接。

在这一应用中,支持取消操作并不像看起来那么容易。一个显而易见的方法是,每次移动时保存移动的距离,而在取消这次移动时该对象移回相等的距离,而在取消这次移动时该对象移回相等的距离。然而,这并不能保证所有的对象都会出现在它们原先出现的地方。设想在移动过程中某连接中有一些松弛。在这种情况下,简单地将矩形移回它原来的位置并不一定能得到预想的结果。
在这里插入图片描述
一般来说,ConstraintSolver 的公共接口可能不足以精确地逆转它对其他对象的作用。为重建先前的状态,取消操作机制必须与 ConstraintSolver 更紧密地结合,但我们同时也应避免将 ConstraintSolver 的内部暴露给取消操作机制。

我们可以用备忘录模式解决这一问题。一个备忘录是一个对象,它存储另一个对象在某个瞬间的内部状态,而后者成为备忘录的原发器(originator)。当需要设置该原发器的检查点时,取消操作机制会向原发器请求一个备忘录。原发器用描述当前状态的信息初始化该备忘录。只有原发器可以向备忘录中存取信息,备忘录对其他的对象 不可见

在前面的例子中,ConstraintSolver 可作为一个原发器。下面的事件顺序描述了取消操作的过程:

  1. 作为移动操作的一个副本,编辑器向 ConstraintSolver 请求一个备忘录。
  2. ConstraintSolver 创建并返回一个备忘录。在这个例子中该备忘录是 SolverState 类的一个实例。SolverState 备忘录包含一些描述 ConstraintSolver 的内部等式和变量当前状态的数据结构。
  3. 此后用户取消移动操作时,编辑器将 SolverState 备忘录送回给 ConstraintSolver
  4. 根据 SolverState 备忘录中的信息,ConstraintSolver改变它的内部结构以精确地将它的等式和变量返回到它们各自先前的状态。

这一方案允许 ConstraintSolver 把恢复向前状态所需的信息提交给其它的对象,而又不暴露它的内部结构和表示。

二、适用性

  • 必须保存一个对象在某个时刻的状态,这样以后需要时它才能恢复到先前的状态。
  • 如果一个接口让其他对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

三、结构

在这里插入图片描述

四、效果

1、保持封装边界

使用备忘录可以避免暴露一些只应由原发器管理却又必须存储在原发器之外的信息。该模式把可能复杂的 Originator 内部信息对其他对象屏蔽起来,从而保持了封装边界。

2、简化了原发器

在其他的保持封装性的设计中,Originator 负责保持客户请求过的内部版本状态。这就把所有存储管理的重任交给了 Originator。让客户管理请求的状态将会简化 Originator,并且使得客户工作结束时无需通知原发器。

3、使用备忘录可能代价很高

如果原发器在生成备忘录时必须拷贝并存储大量的信息,或者客户非常频繁地创建备忘录和恢复原发器状态,可能会导致非常大的开销。除非封装和恢复 Originator 状态的开销不大,否则该模式可能并不合适。

4、定义窄接口和宽接口

在一些语言中可能难以保证只有原发器可以访问备忘录的状态。

5、维护备忘录的签字按代价

管理者负责删除它所维护的备忘录。然而, 管理者不知道备忘录中有多少个状态。因此当存储备忘录时,一个本来很小的管理者可能会产生大量的存储开销。

五、实现

1、语言支持

在C++中,可以借助友元避免原发器的宽接口暴露。

2、存储增量式改变

如果备忘录的创建及其返回(给原发器)的顺序是可预测的,备忘录可以仅存储原发器内部状态的增量改变。

例如,一个包含可撤销的命令的历史列表可使用备忘录,以保证命令被取消时它们可以恢复到正确的状态。历史列表定义两个一个特定的顺序,按照这个顺序命令可以被撤销和重做。这意味着备忘录可以只存储一个命令所产生的增量改变而不是它所影响的每一个对象的完整状态。在前面动机一节给出的例子中,约束解释器可以仅存储那些变化了的内部结构,以保持直线与矩形相连,而不是存储这些对象的绝对位置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值