19.2 解决方案
19.2.1 备忘录模式来解决
来解决上述问题的一个合理的解决方案就是备忘录模式。那么什么是备忘录模式呢?
(1)备忘录模式定义
一个备忘录是一个对象,它存储另一个对象在某个瞬间的内部状态,后者被称为备忘录的原发器。
(2)应用备忘录模式来解决的思路
仔细分析上面的示例功能,需要在运行期间捕获模拟流程运行的对象的内部状态,这些需要捕获的内部状态就是它运行第一个阶段产生的内部数据,并且在该对象之外来保存这些状态,因为在后面它有不同的运行方案。但是这些不同的运行方案需要的初始数据是一样的,都是流程在第一个阶段运行所产生的数据,这就要求运行每个方案后半部分前,要把该对象的状态恢复回到第一个阶段运行结束时候的状态。
在这个示例中出现的、需要解决的问题就是:如何能够在不破坏对象的封装性的前提下,来保存和恢复对象的状态。
看起来跟备忘录模式要解决的问题是如此的贴切,简直备忘录模式像是专为这个应用打造的一样。那么使用备忘录模式如何来解决这个问题呢?
备忘录模式引入一个存储状态的备忘录对象,为了让外部无法访问这个对象的值,一般把这个对象实现成为需要保存数据的对象的内部类,通常还是私有的,这样一来,除了这个需要保存数据的对象,外部无法访问到这个备忘录对象的数据,这就保证了对象的封装性不被破坏。
但是这个备忘录对象需要存储在外部,为了避免让外部访问到这个对象内部的数据,备忘录模式引入了一个备忘录对象的窄接口,这个接口一般是空的,什么方法都没有,这样外部存储的地方,只是知道存储了一些备忘录接口的对象,但是由于接口是空的,它们无法通过接口去访问备忘录对象内的数据。
19.2.2 模式结构和说明
备忘录模式结构如图19.1所示:
图19.1 备忘录模式结构示意图
Memento:
备忘录。主要用来存储原发器对象的内部状态,但是具体需要存储哪些数据是由原发器对象来决定的。另外备忘录应该只能由原发器对象来访问它内部的数据,原发器外部的对象不应该能访问到备忘录对象的内部数据。
Originator:
原发器。使用备忘录来保存某个时刻原发器自身的状态,也可以使用备忘录来恢复内部状态。
Caretaker:
备忘录管理者,或者称为备忘录负责人。主要负责保存备忘录对象,但是不能对备忘录对象的内容进行操作或检查。
19.2.3 备忘录模式示例代码
(1)先看看备忘录对象的窄接口,就是那个Memento接口,这个实现最简单,是个空的接口,没有任何方法定义,示例代码如下:
/** * 备忘录的窄接口,没有任何方法定义 */ public interface Memento { // } |
(2)看看原发器对象,它里面会有备忘录对象的实现,因为真正的备忘录对象当作原发器对象的一个私有内部类来实现了。示例代码如下:
/** * 原发器对象 */ public class Originator { /** * 示意,表示原发器的状态 */ private String state = ""; /** * 创建保存原发器对象的状态的备忘录对象 * @return 创建好的备忘录对象 */ public Memento createMemento() { return new MementoImpl(state); } /** * 重新设置原发器对象的状态,让其回到备忘录对象记录的状态 * @param memento 记录有原发器状态的备忘录对象 */ public void setMemento(Memento memento) { MementoImpl mementoImpl = (MementoImpl)memento; this.state = mementoImpl.getState(); } /** * 真正的备忘录对象,实现备忘录窄接口 * 实现成私有的内部类,不让外部访问 */ private static class MementoImpl implements Memento{ /** * 示意,表示需要保存的状态 */ private String state = ""; public MementoImpl(String state){ this.state = state; } public String getState() { return state; } } } |
(3)接下来看看备忘录管理者对象,示例代码如下:
/** * 负责保存备忘录的对象 */ public class Caretaker{ /** * 记录被保存的备忘录对象 */ private Memento memento = null; /** * 保存备忘录对象 * @param memento 被保存的备忘录对象 */ public void saveMemento(Memento memento){ this.memento = memento; } /** * 获取被保存的备忘录对象 * @return 被保存的备忘录对象 */ public Memento retriveMemento(){ return this.memento; } } |
19.2.4 使用备忘录模式重写示例
学习了备忘录模式的基本知识过后,来尝试一下,使用备忘录模式把前面的示例重写一下,好看看如何使用备忘录模式。
- 首先,那个模拟流程运行的对象,就相当于备忘录模式中的原发器;
- 而它要保存的数据,原来是零散的,现在做一个备忘录对象来存储这些数据,并且把这个备忘录对象实现成为内部类;
- 当然为了保存这个备忘录对象,还是需要提供管理者对象的;
- 为了和管理者对象交互,管理者需要知道保存对象的类型,那就提供一个备忘录对象的窄接口来供管理者使用,相当于标识了类型。
此时程序的结构如图19.2所示:
图19.2 使用备忘录模式重写示例的结构示意图
(1)先来看看备忘录对象的窄接口吧,示例代码如下:
/** * 模拟运行流程A的对象的备忘录接口,是个窄接口 */ public interface FlowAMockMemento { //空的 } |
(2)再来看看新的模拟运行流程A的对象,相当于原发器对象了,它的变化比较多,大致有如下变化:
- 首先这个对象原来暴露出去的内部状态,不用再暴露出去了,也就是内部状态不用再对外提供getter/setter方法了
- 在这个对象里面提供一个私有的备忘录对象,里面封装想要保存的内部状态,同时让这个备忘录对象实现备忘录对象的窄接口
- 在这个对象里面提供创建备忘录对象,和根据备忘录对象恢复内部状态的方法
具体的示例代码如下:
/** * 模拟运行流程A,只是一个示意,代指某个具体流程 */ public class FlowAMock { /** * 流程名称,不需要外部存储的状态数据 */ private String flowName; /** * 示意,代指某个中间结果,需要外部存储的状态数据 */ private int tempResult; /** * 示意,代指某个中间结果,需要外部存储的状态数据 */ private String tempState; /** * 构造方法,传入流程名称 * @param flowName 流程名称 */ public FlowAMock(String flowName){ this.flowName = flowName; } /** * 示意,运行流程的第一个阶段 */ public void runPhaseOne(){ //在这个阶段,可能产生了中间结果,示意一下 tempResult = 3; tempState = "PhaseOne"; } /** * 示意,按照方案一来运行流程后半部分 */ public void schema1(){ //示意,需要使用第一个阶段产生的数据 this.tempState += ",Schema1"; System.out.println(this.tempState + " : now run "+tempResult); this.tempResult += 11; } /** * 示意,按照方案二来运行流程后半部分 */ public void schema2(){ //示意,需要使用第一个阶段产生的数据 this.tempState += ",Schema2"; System.out.println(this.tempState + " : now run "+tempResult); this.tempResult += 22; } /** * 创建保存原发器对象的状态的备忘录对象 * @return 创建好的备忘录对象 */ public FlowAMockMemento createMemento() { return new MementoImpl(this.tempResult,this.tempState); } /** * 重新设置原发器对象的状态,让其回到备忘录对象记录的状态 * @param memento 记录有原发器状态的备忘录对象 */ public void setMemento(FlowAMockMemento memento) { MementoImpl mementoImpl = (MementoImpl)memento; this.tempResult = mementoImpl.getTempResult(); this.tempState = mementoImpl.getTempState(); } /** * 真正的备忘录对象,实现备忘录窄接口 * 实现成私有的内部类,不让外部访问 */ private static class MementoImpl implements FlowAMockMemento{ /** * 示意,保存某个中间结果 */ private int tempResult; /** * 示意,保存某个中间结果 */ private String tempState; public MementoImpl(int tempResult,String tempState){ this.tempResult = tempResult; this.tempState = tempState; } public int getTempResult() { return tempResult; } public String getTempState() { return tempState; } } } |
(3)接下来要来实现提供保存备忘录对象的管理者了,示例代码如下:
/** * 负责保存模拟运行流程A的对象的备忘录对象 */ public class FlowAMementoCareTaker { /** * 记录被保存的备忘录对象 */ private FlowAMockMemento memento = null; /** * 保存备忘录对象 * @param memento 被保存的备忘录对象 */ public void saveMemento(FlowAMockMemento memento){ this.memento = memento; } /** * 获取被保存的备忘录对象 * @return 被保存的备忘录对象 */ public FlowAMockMemento retriveMemento(){ return this.memento; } } |
(4)最后来看看,如何使用上面按照备忘录模式实现的这些对象呢,写个新的客户端来测试一下,示例代码如下:
public class Client { public static void main(String[] args) { // 创建模拟运行流程的对象 FlowAMock mock = new FlowAMock("TestFlow"); //运行流程的第一个阶段 mock.runPhaseOne(); //创建一个管理者 FlowAMementoCareTaker careTaker = new FlowAMementoCareTaker(); //创建此时对象的备忘录对象,并保存到管理者对象那里,后面要用 FlowAMockMemento memento = mock.createMemento(); careTaker.saveMemento(memento);
//按照方案一来运行流程后半部分 mock.schema1();
//从管理者获取备忘录对象,然后设置回去, //让模拟运行流程的对象自己恢复自己的内部状态 mock.setMemento(careTaker.retriveMemento());
//按照方案二来运行流程后半部分 mock.schema2(); } } |
运行结果跟前面的示例是一样的,结果如下:
PhaseOne,Schema1 : now run 3 PhaseOne,Schema2 : now run 3 |
好好体会一下上面的示例,由于备忘录对象是一个私有的内部类,外面只能通过备忘录对象的窄接口来获取备忘录对象,而这个接口没有任何方法,仅仅起到了一个标识对象类型的作用,从而保证内部的数据不会被外部获取或是操作,保证了原发器对象的封装性,也就不再暴露原发器对象的内部结构了。
相关推荐
备忘录模式.rar备忘录模式.rar备忘录模式.rar
备忘录模式的示例代码和文档,学习备忘录模式的参考资料。
设计模式之备忘录模式 设计模式之备忘录模式 设计模式之备忘录模式 设计模式之备忘录模式 设计模式之备忘录模式
备忘录模式Demo
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 备忘录模式java demo
java设计模式-备忘录模式源代码, 源码中为设计模式的工程源文件,已经测试过,没有问题。
C#备忘录模式 代码例子 写的比较乱,呵呵
设计模式-备忘录模式(讲解及其实现代码)
设计模式之备忘录模式的Java版本实现和UML类设计图
本例包含备忘录模式的C#实现,及一个应用案例,加深理解与运用。
2、备忘录模式: *备忘录模式的优点有: *当发起人角色中的状态改变时,有可能这是个错误的改变,我们使用备忘录模式就可以把这个错误的改变还原。 *备份的状态是保存在发起人角色之外的,这样,发起人角色就不...
备忘录模式C++实现,若理解有误,请留言指出。
备忘录模式 C++ 实现
备忘录模式在不破坏封装的前提下,记录一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态,本例中设置历次成绩,并记录和保存,最后返回最好成绩
ios平台下备忘录模式最初简代码讲解何为备忘录模式,相关博客http://blog.sina.com.cn/s/blog_161d504630102wxis.html
C#面向对象设计模式纵横谈(21):(行为型模式) Memento 备忘录模式
设计模式专题之(十九)备忘录模式--设计模式备忘录模式示例代码(python--c++)
设计模式之备忘录 和 状态模式精讲 19.1 场景问题 19.1.1 开发仿真系统 考虑这样一个仿真应用,功能是:模拟运行针对某个具体问题的多个解决方案,记录运行过程的各种数据,在模拟运行完成过后,好对这多个解决...
Android备忘录模式简单Demo,实现通过memoto备忘录实体,实现游戏存档和读档的功能简单模拟,便于理解备忘录模式.同步博文:http://blog.csdn.net/qq_28690547/article/details/50708415
设计模式-备忘录模式