[格物致知|深入浅出|学以致用]
1. 定义
所谓备忘录模式,就是在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。
2. 格物
对于备忘录模式,有几个点需要注意:
- 不破坏封闭
- 对象的内部状态
- 在该对象之外
这三个要点是从备忘录模式的定义中提炼出来的,但如果要更容易的理解,应该把它们的顺序颠倒一下。如下:
- 在该对象之外
- 对象的内部状态
- 不破坏封闭
下面我们来依次分析一下这几个要点。
2.1. 在该对象之外
这个点其实是要求将数据模型类和数据的保存操作解耦合,不由该对象自己进行数据的持久化,以免具体的保存逻辑出现变动时而改动该类的实现。
这个要点其实是其他设计模式和原则的要求。
2.2. 对象的内部状态
众所周知,在面向对象概念的类定义中,存在私有变量和公有变量之分,而备忘录模式定义中所强调的“对象的内部状态”应该就是指类似的私有变量。对象的内部状态只是跟对象自己的行为有关,所以并没有开放给其他类的实例访问。
但是如果想要保存某一个状态下的完整对象,私有变量的值也是需要保存下来的。
这个要点是该设计模式的前提。
2.3. 不破坏封闭
这个要点是该设计模式的精髓所在,有了前两个的铺垫,那这个要点就很容易理解了。
问题:在一个对象之外对该对象的内部私有变量并进行保存。
一般遇到这样的问题,常用的处理方法有两种:
第一,违背要点一,改由该对象自己进行数据保存,并新增一个公用方法给外部调用;
第二,违背要点二,将类的私有变量改为公有变量,让外部的对象直接访问,并进行保存。
看到这里有人不禁要问,上面这两个方法已经违背了这个设计模式定义的前提,怎么叫解决方法呢?作者你难道在讲笑话?
其实不然,现实的编程工作中有这无数的迁就和妥协,而上面这两个方法就是现实的写照~(此处应该有憨笑emoji)
或许Gof为了避免大家使用这两个取巧的办法,所以加了第三个要点作为强调——“不破坏封闭”。
这样就要求我们直面这个貌似悖论的问题:如何在对象外保存对象的私有变量?
3.致知
有了对前面三个要点的分析,那么现状就很明了了。
对象外部需要保存对象内部变量,两者无法直接沟通,那么必然需要一个中间产物来作为中介。即对象将内部变量放在中间产物中,然后交由对象外部,对象外部将该中间产物进行保存。
而数据恢复时,对象外部先读取到中间产物,然后将中间产物交给对象,对象自己恢复中间产物中的数据。
这样一来就完全满足了模式定义中的三个要点,没有破坏封闭,在对象外部保存对象的内部变量。
而这里所谓的中间产物,便是该模式的名称——备忘录。
备忘录模式中的三个角色分别为发起人Originator、备忘录Memento、管理者Caretaker。这些角色分类充分体现了设计模式的单一职责原则,发起人负责业务功能,向业务系统提供数据;备忘录只负责为保存功能提供组织好的数据;管理者负责具体的保存功能。
备忘录为了满足信息的封闭,即部分数据向发起人开放,而不向管理者开放,正常来说需要两个接口,这便是常说的宽接口和窄接口。宽接口提供完整的数据访问,以便发起人能够据此恢复数据。而管理者只需要保存备忘录本身,所以只需要一个最小的窄接口即可。
4.致用
根据备忘录模式的定义,我们很容易便可以找到该模式适用的第一个场景——1.需要在对象外保存对象的内部变量时。
不过设计模式的意义在于举一反三,所以其可以应用的外延大多超过了其定义。
即便现在的应用场景不是保存对象的内部变量,而是保存对象的公开变量,从迪米特法则来看,也不应该由管理者直接进行保存,而应该由发起人自己保存。但发起人自己保存,又违反了单一职责原则。若是由管理者直接将发起人保存,那样当需要保存发起人的部分变量时,这样的功能又有些冗余。如此一来,便又回到了备忘录模式上——2.需要保存对象的部分变量时。