首页 理论教育 备忘录模式:面向对象程序设计通俗演义

备忘录模式:面向对象程序设计通俗演义

时间:2023-11-03 理论教育 版权反馈
【摘要】:第十八章月光宝盒——备忘录模式18.1月光宝盒时间:1月3日地点:大B房间人物:大B,小A这天,大B和小A聊起了《大话西游》。如果真的有《大话西游》中能时光倒流的“月光宝盒”,那这世上也许会少一些伤感与后悔——当然这只能是痴人说梦了。我们讲的备忘录模式便是程序世界里的“月光宝盒”。

备忘录模式:面向对象程序设计通俗演义

第十八章 月光宝盒——备忘录模式

18.1 月光宝盒

时间:1月3日  地点:大B房间  人物:大B,小A

这天,大B和小A聊起了《大话西游》。

小A:“师兄,你看过《大话西游》吗?”

大B:“看过好几回哩,挺好看的。”

小A:“是啊!我也是这么觉得。”

小A:“嘿嘿!如果时间可以倒流就好了。”

大B:“你是不是看《大话西游》看晕了?”

小A:“是啊!如果我也有个月光宝盒那好了。”

大B:“是不是你也想来个时间倒流,然后也来个跨越时空的爱情?”

小A:“呵呵。如果可能那当然好。”

18.2 备忘录模式

俗话说:世上难买后悔药。所以凡事讲究个“三思而后行”,但总常见有人做“痛心疾首”状:当初我要是……。如果真的有《大话西游》中能时光倒流的“月光宝盒”,那这世上也许会少一些伤感与后悔——当然这只能是痴人说梦了。

但是在我们手指下的程序世界里,却有的后悔药买。我们讲的备忘录模式便是程序世界里的“月光宝盒”。

小A:“什么叫备忘录模式?”

大B:“备忘录(Memento)模式又称标记(Token)模式。在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。在讲命令模式的时候,我们曾经提到利用中间的命令角色可以实现undo、redo的功能。从定义可以看出备忘录模式是专门来存放对象历史状态的,这对于很好的实现undo、redo功能有很大的帮助。所以在命令模式中undo、redo功能可以配合备忘录模式来实现。其实单就实现保存一个对象在某一时刻的状态的功能,还是很简单的——将对象中要保存的属性放到一个专门管理备份的对象中,需要的时候则调用约定好的方法将备份的属性放回到原来的对象中去。但是你要好好看看为了能让你的备份对象访问到原对象中的属性,是否意味着你就要全部公开或者包内公开对象原本私有的属性呢?如果你的做法已经破坏了封装,那么就要考虑重构一下了。”

18.3 使用备忘录模式的原因

小A:“为什么要使用备忘录模式?”

大B:“想要恢复对象某时的原有状态。”

18.4 备忘录模式适用的情况举例

小A:“能不能举些例子说一下备忘录模式适用的情况?”

大B:“有很多备忘录模式的应用,只是我们已经见过,却没细想这是备忘录模式的使用罢了,略略举几例:1、备忘录在jsp+javabean的使用:在一系统中新增帐户时,在表单中需要填写用户名、密码、联系电话、地址等信息,如果有些字段没有填写或填写错误,当用户点击‘提交’按钮时,需要在新增页面上保存用户输入的选项,并提示出错的选项。这就是利用JavaBean的scope=‘request’或scope=‘session’特性实现的,即是用备忘录模式实现的。2、修理汽车刹车时。首先移开两边的挡板,露出左右刹车片。只能卸下一片,这时另一片作为一个备忘录来表明刹车是怎样安装的。在这片修理完成后,才可以卸下另一片。当第二片卸下时,第一片就成了备忘录。3、都说人生没有后悔药可买,我们都在为所做的事付出着代价,但在软世界里却有‘后悔药’,我改变了某东西的某些状态之后,只要我们之前保存了该东西的某状态,我们就可以通过备忘录模式实现该东西的状态还原,其实这何尝不是一个能使时光倒流的‘月光宝盒’,总‘神奇’一词了得。”

18.5  “月光宝盒”备忘录模式的组成部分

小A:“那‘月光宝盒’备忘录模式有哪些组成部分?”

大B:“1、备忘录(Memento)角色:备忘录角色存储‘备忘发起角色’的内部状态。‘备忘发起角色’根据需要决定备忘录角色存储‘备忘发起角色’的哪些内部状态。为了防止‘备忘发起角色’以外的其他对象访问备忘录。备忘录实际上有两个接口,‘备忘录管理者角色’只能看到备忘录提供的窄接口——对于备忘录角色中存放的属性是不可见的。‘备忘发起角色’则能够看到一个宽接口——能够得到自己放入备忘录角色中属性。2、备忘发起(Originator)角色:‘备忘发起角色’创建一个备忘录,用以记录当前时刻它的内部状态。在需要时使用备忘录恢复内部状态。3、备忘录管理者(Caretaker)角色:负责保存好备忘录。不能对备忘录的内容进行操作或检查。我们称它为宽接口;而另一个则可以只是一个标示,我们称它为窄接口。备忘录角色要实现这两个接口类。这样对于‘备忘发起角色’采用宽接口进行访问,而对于其他的角色或者对象则采用窄接口进行访问。这种实现比较简单,但是需要人为的进行规范约束——而这往往是没有力度的。第二种方法便很好的解决了第一种的缺陷:采用内部类来控制访问权限。将备忘录角色作为‘备忘发起角色’的一个私有内部类。好处我不详细解释了,看看代码吧就明白了。下面的代码是一个完整的备忘录模式的教学程序。它便采用了第二种方法来实现备忘录模式。还有一点值得指出的是,在下面的代码中,对于客户程序来说‘备忘录管理者角色’是不可见的,这样简化了客户程序使用备忘录模式的难度。下面采用‘备忘发起角色’来调用访问‘备忘录管理者角色’,也可以参考门面模式在客户程序与备忘录角色之间添加一个门面角色。”

大B:“第三种方式是不太推荐使用的:使用clone方法来简化备忘录模式。由于Java提供了clone机制,这使得复制一个对象变得轻松起来。使用了clone机制的备忘录模式,备忘录角色基本可以省略了,而且可以很好的保持对象的封装。但是在为你的类实现clone方法时要慎重啊。 在上面的代码中,我们简单的模拟了备忘录模式的整个流程。在实际应用中,我们往往需要保存大量‘备忘发起角色’的历史状态。这时就要对我们的‘备忘录管理者角色’进行改造,最简单的方式就是采用容器来按照顺序存放备忘录角色。这样就可以很好的实现undo、redo功能了。”

18.6 备忘录模式适用情况

大B:“使用了备忘录模式来实现保存对象的历史状态可以有效地保持封装边界。使用备忘录可以避免暴露一些只应由‘备忘发起角色’管理却又必须存储在‘备忘发起角色’之外的信息。把‘备忘发起角色’内部信息对其他对象屏蔽起来, 从而保持了封装边界。但是如果备份的‘备忘发起角色’存在大量的信息或者创建、恢复操作非常频繁,则可能造成很大的开销。”

小A:“ 那使用备忘录模式的前提是什么?”

大B:“1、必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。(www.xing528.com)

2、如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。”

18.7 备忘录模式的实现

小A:“备忘录模式要怎么去实现?”

大B:“我详细给你说。”

1、备忘录模式中的角色

发起人:创建含有内部状态的备忘录对象,并使用备忘录对象存储状态

负责人:负责人保存备忘录对象,但不检查备忘录对象的内容

备忘录:备忘录对象将发起人对象的内部状态存起来,并保正其内容不被发起人对象之外的对象像读取

注意:在备忘录的角色中,定义了他必须对不同的人提供不同的接口,对发起人提供宽接口,对其它任何人提供窄

接口。也许你说我都提供宽接口得了。对这也是备忘录的一种实现,叫做白箱备忘录,不过这种方法的封装没有设计

好,安全性不够好。

2、白箱备忘录的实现:

白箱的优点:实现简单

白箱的缺点:上边说了,破坏了封装,安全性有些问题。

说明:这里白箱的实现只保存了一个状态,其实是可以保存多个状态的。

3、双接口的实现,宽窄接口(黑箱)

如何实现宽窄接口呢,内部类也许是个好方法。我们把备忘录类设计"成发起人"的内部类,但这样还有的问题是同一package中的其它类也能访问到,为了解决这个问题,我们可以把"备忘录"的方法设计成私有的方法,这样就可以保正封装,又保正发起人能访问到。实现如下:

定义窄接口。

大B:“还要注意的是:1、前边两个例子都是记录了单个状态(单check点),要实现多个状态点很容易,只须要把记录state的字符串换成一个list,然後添加,取得。如果须要随机须得状态点,也可以用map来存放.这样多个check点就实现了。2、一般情况下可以扩展负责人的功能,让负责人的功能更强大,从而让客户端的操做更少些。解放客户端。3、自述历史模式,这个就是把发起人,负责人写在一个类中,平时的应用中这种方法比较常见。”

18.8 备忘录角色的作用

小A:“备忘录角色有什么作用啊?”

大B:“1、将发起人对象的内部状态存储起来,备忘录能根据发起人对象的判断来决定存储多少发起人对象的内部状态。2、备忘录能保护其内容不被发起人对象之外的所有对象所读取。”

小A:“发起人角色有什么作用?”

大B:“1、创建一个含有当前内部状态的备忘录对象。2、使用备忘录对象存储其内部状态。”

小A:“那负责人角色又有什么作用?”

大B:“负责保存备忘录对象和不检查备忘录对象的内容。”

18.9 备忘录模式与命令模式的区别

小A:“备忘录模式与命令模式有一些相似之处,他们都保存状态,他们都可以拥有前进与后退, 但是他们到底在设计上与实现上有哪些差别呢?”

大B:“1、异同点:从UML我们可以清晰的看到区别,一个保存Object的状态,一个保存命令。相同:都可以前进后退。不同:执行对象不同,保存状态的对象不通,所执行的操作也不相同。由于两种模式时所对应的需求截然不同,应该说备忘录更加稳定一些,而命令的执行则更加广泛,可能一个子类的Command对应一个Receiver。所以相对而言Command模式会更加灵活一些。应用:Command模式:将命令当作一个对象进行保存,进行Redo ,Undo操作。”

例子:在绘图系统中经常需要进行Redo,Undo操作。Memento模式:获取和保存对象的内部状态。例子:网上购物时购物车既可以理解为Memento。”

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈