java语音备忘录_Java 设计模式——备忘录模式

在阎宏博士的《JAVA 与模式》一书中开头是这样描述备忘录(Memento)模式的:

备忘录模式又叫做快照模式 (Snapshot Pattern) 或 Token 模式,是对象的行为模式。

备忘录对象是一个用来存储另外一个对象内部状态快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉 (Capture) 住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。备忘录模式常常与命令模式和迭代子模式一同使用。

备忘录模式的结构

备忘录模式的结构图如下所示

42cfdd58c4351dc50740f962a214986d.png

备忘录模式所涉及的角色有三个:

备忘录 (Memento) 角色

发起人 (Originator) 角色

负责人 (Caretaker) 角色。

备忘录 (Memento) 角色

备忘录角色又如下责任:

(1)将发起人(Originator)对象的内战状态存储起来。备忘录可以根据发起人对象的判断来决定存储多少发起人(Originator)对象的内部状态。

(2)备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。

备忘录有两个等效的接口:

窄接口:负责人(Caretaker)对象(和其他除发起人对象之外的任何对象)看到的是备忘录的窄接口 (narrow interface),这个窄接口只允许它把备忘录对象传给其他的对象。

宽接口:与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口 (wide interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。

发起人(Originator)角色

发起人角色有如下责任:

(1)创建一个含有当前的内部状态的备忘录对象。

(2)使用备忘录对象存储其内部状态。

负责人(Caretaker)角色

负责人角色有如下责任:

(1)负责保存备忘录对象。

(2)不检查备忘录对象的内容。

“白箱”备忘录模式的实现

备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开。因此这个实现又叫做“白箱实现”。

“白箱”实现将发起人角色的状态存储在一个大家都看得到的地方,因此是破坏封装性的。但是通过程序员自律,同样可以在一定程度上实现模式的大部分用意。因此白箱实现仍然是有意义的。

下面给出一个示意性的“白箱实现”。

df748368e4d3149791559c4fcf561cf9.png

源代码

发起人角色类

** 发起人角色利用一个新创建的备忘录对象将自己的内部状态存储起来。**

e85650d802f0029e819a5a1136e842e9.png

备忘录角色类

** 备忘录对象将发起人对象传入的状态存储起来。**

cfe9e714489e605c36caa522634a1fdc.png

负责人角色类

** 负责人角色负责保存备忘录对象,但是从不修改(甚至不查看)备忘录对象的内容。**

124f41dbd80a9ac6b9a974856ef04565.png

客户端角色类

d281eef56ab3252ddd81d239b560853e.png

在上面的这个示意性的客户端角色里面,首先将发起人对象的状态设置成“On”,并创建一个备忘录对象将这个状态存储起来;然后将发起人对象的状态改成“Off”;最后又将发起人对象恢复到备忘录对象所存储起来的状态,即“On”状态。

系统的时序图更能够反映出系统各个角色被调用的时间顺序。如下图是将发起人对象的状态存储到白箱备忘录对象中去的时序图。

d8321f1f9133a079f3644900a84e4ac2.png

可以看出系统运行的时序是这样的:

(1)将发起人对象的状态设置成“On”。

(2)调用发起人角色的 createMemento() 方法,创建一个备忘录对象将这个状态存储起来。

(3)将备忘录对象存储到负责人对象中去。

将发起人对象恢复到备忘录对象所记录的状态的时序图如下所示:

85ef2bd45e1f7d28110ac4d343b030da.png

可以看出,将发起人对象恢复到备忘录对象所记录的状态时,系统的运行时序是这样的:

(1)将发起人状态设置成“Off”。

(2)将备忘录对象从负责人对象中取出。

(3)将发起人对象恢复到备忘录对象所存储起来的状态,即“On”状态。

“黑箱”备忘录模式的实现

备忘录角色对发起人(Originator)角色对象提供一个宽接口,而为其他对象提供一个窄接口。这样的实现叫做“黑箱实现”。

在 JAVA 语言中,实现双重接口的办法就是将备忘录角色类设计成发起人角色类的内部成员类。

将 Memento 设成 Originator 类的内部类,从而将 Memento 对象封装在 Originator 里面;在外部提供一个标识接口 MementoIF 给 Caretaker 以及其他对象。这样,Originator 类看到的是 Menmento 的所有接口,而 Caretaker 以及其他对象看到的仅仅是标识接口 MementoIF 所暴露出来的接口。

使用内部类实现备忘录模式的类图如下所示:

1c0a74ebcfbfb347d895c2e9599064bf.png

源代码

发起人角色类

发起人角色类 Originator 中定义了一个内部的 Memento 类。由于此 Memento 类的全部接口都是私有的,因此只有它自己和发起人类可以调用。

3400abf84fd9148d979a9dbf3032e2a9.png

5ceef5fc212641535995229e5cb978be.png

窄接口 MementoIF

窄接口 MementoIF 是一个标识接口,因此它没有定义出任何的方法。

8d7cc8e0eeade802f3ded4b367b75672.png

负责人角色类

负责人角色类 Caretaker 能够得到的备忘录对象是以 MementoIF 为接口的,由于这个接口仅仅是一个标识接口,因此负责人角色不可能改变这个备忘录对象的内容。

63eac126248b2a5d3be7f30b4a375e1a.png

客户端角色类

d3fd960b7c1ba9f4de17d890fb8e9b36.png

客户端首先

(1)将发起人对象的状态设置为“On”。

(2)调用 createMemento()方法,创建一个备忘录对象将这个状态存储起来(此时 createMemento() 方法还回的明显类型是 MementoIF 接口,真实类型为 Originator 内部的 Memento 对象)。

(3)将备忘录对象存储到负责人对象中去。由于负责人对象拿到的仅是 MementoIF 接口,因此无法读出备忘录对象内部的状态。

(4)将发起人对象的状态设置为“Off”。

(5)调用负责人对象的 retrieveMemento() 方法将备忘录对象取出。注意此时仅能得到 MementoIF 接口,因此无法读出此对象的内部状态。

(6)调用发起人对象的 restoreMemento() 方法将发起人对象的状态恢复成备忘录对象所存储的起来的状态,即“On”状态。由于发起人对象的内部类 Memento 实现了 MementoIF 接口,这个内部类是传入的备忘录对象的真实类型,因此发起人对象可以利用内部类 Memento 的私有接口读出此对象的内部状态。

多重检查点

前面所给出的白箱和黑箱的示意性实现都是只存储一个状态的简单实现,也可以叫做只有一个检查点。常见的系统往往需要存储不止一个状态,而是需要存储多个状态,或者叫做有多个检查点。

备忘录模式可以将发起人对象的状态存储到备忘录对象里面,备忘录模式可以将发起人对象恢复到备忘录对象所存储的某一个检查点上。下面给出一个示意性的、有多重检查点的备忘录模式的实现。

ddc6f344339158e6a6b538ff5650e0ff.png

源代码

发起人角色类

a8026a8b56346a1e2af35874c3c7db09.png

22e15d337cbd6c2bd98085df050f9d61.png

备忘录角色类

备忘录角色类,这个实现可以存储任意多的状态,外界可以使用检查点指数 index 来取出检查点上的状态。

a18e61ec283c4eb4f46fc3f0ec3ae9b1.png

负责人角色类

cf759121cf584f085030facb718555e1.png

bacfd9b349f2a7506665ba44c13528f5.png

客户端角色

a25200b7399cc8c2dd375ca3546ca6b5.png

80a4f367fecd0be52885eb7e0707ceda.png

运行结果

371734b86bf05b18e3994da5431dac04.png

可以看出,客户端角色通过不断改变发起人角色的状态,并将之存储在备忘录里面。通过指明检查点指数可以将发起人角色恢复到相应的检查点所对应的状态上。

将发起人的状态存储到备忘录对象中的活动序列图如下:

972b568837cd232a0b4daccf255ed0ef.png

系统运行的时序是这样的:

(1)将发起人对象的状态设置成某个有效状态;

(2)调用负责人角色的 createMemento() 方法,负责人角色会负责调用发起人角色和备忘录角色,将发起人对象的状态存储起来。

将发起人对象恢复到某一个备忘录对象的检查点的活动序列图如下:

6a37a4d1eed87bd4dbd4c0d2c47863eb.png

由于负责人角色的功能被增强了,因此将发起人对象恢复到备忘录对象所记录的状态时,系统运行的时序被简化了:

调用负责人角色的 restoreMemento() 方法,将发起人恢复到某个检查点。

“自述历史”模式

所谓“自述历史”模式 (History-On-Self Pattern) 实际上就是备忘录模式的一个变种。在备忘录模式中,发起人 (Originator) 角色、负责人 (Caretaker) 角色和备忘录 (Memento) 角色都是独立的角色。虽然在实现上备忘录类可以成为发起人类的内部成员类,但是备忘录类仍然保持作为一个角色的独立意义。在“自述历史”模式里面,发起人角色自己兼任负责人角色。

“自述历史”模式的类图如下所示:

7c454b6265f03bc36f86263fe1f7e68e.png

备忘录角色有如下责任:

将发起人(Originator)对象的内部状态存储起来。

备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取。

** 发起人角色有如下责任:**

创建一个含有它当前的内部状态的备忘录对象。

使用备忘录对象存储其内部状态。

客户端角色有负责保存备忘录对象的责任。

源代码

窄接口 MementoIF

窄接口 MementoIF 是一个标识接口,因此它没有定义出任何的方法。

c7830821121151d4ebbe44d86342f3de.png

发起人角色

发起人角色同时还兼任负责人角色,也就是说它自己负责保持自己的备忘录对象。

f9a65729554b64ecfcf19dfdafb8b6bb.png

721dc827145a7c7877fb58725c7bd577.png

客户端角色类

daca85cbfddbfbbd9536b2dcb543ab34.png

由于“自述历史”作为一个备忘录模式的特殊实现形式非常简单易懂,它可能是备忘录模式最为流行的实现形式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值