首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

备忘录方式2

2013-12-13 
备忘录模式219.2? 解决方案19.2.1? 备忘录模式来解决来解决上述问题的一个合理的解决方案就是备忘录模式。

备忘录模式2
19.2? 解决方案19.2.1? 备忘录模式来解决

来解决上述问题的一个合理的解决方案就是备忘录模式。那么什么是备忘录模式呢?

(1)备忘录模式定义

?备忘录方式2

一个备忘录是一个对象,它存储另一个对象在某个瞬间的内部状态,后者被称为备忘录的原发器。

(2)应用备忘录模式来解决的思路

??? 仔细分析上面的示例功能,需要在运行期间捕获模拟流程运行的对象的内部状态,这些需要捕获的内部状态就是它运行第一个阶段产生的内部数据,并且在该对象之外来保存这些状态,因为在后面它有不同的运行方案。但是这些不同的运行方案需要的初始数据是一样的,都是流程在第一个阶段运行所产生的数据,这就要求运行每个方案后半部分前,要把该对象的状态恢复回到第一个阶段运行结束时候的状态。

?????? 在这个示例中出现的、需要解决的问题就是:如何能够在不破坏对象的封装性的前提下,来保存和恢复对象的状态。

?????? 看起来跟备忘录模式要解决的问题是如此的贴切,简直备忘录模式像是专为这个应用打造的一样。那么使用备忘录模式如何来解决这个问题呢?

?????? 备忘录模式引入一个存储状态的备忘录对象,为了让外部无法访问这个对象的值,一般把这个对象实现成为需要保存数据的对象的内部类,通常还是私有的,这样一来,除了这个需要保存数据的对象,外部无法访问到这个备忘录对象的数据,这就保证了对象的封装性不被破坏。

?????? 但是这个备忘录对象需要存储在外部,为了避免让外部访问到这个对象内部的数据,备忘录模式引入了一个备忘录对象的窄接口,这个接口一般是空的,什么方法都没有,这样外部存储的地方,只是知道存储了一些备忘录接口的对象,但是由于接口是空的,它们无法通过接口去访问备忘录对象内的数据。

19.2.2? 模式结构和说明

备忘录模式结构如图19.1所示:

?备忘录方式2

图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

    ?????? 好好体会一下上面的示例,由于备忘录对象是一个私有的内部类,外面只能通过备忘录对象的窄接口来获取备忘录对象,而这个接口没有任何方法,仅仅起到了一个标识对象类型的作用,从而保证内部的数据不会被外部获取或是操作,保证了原发器对象的封装性,也就不再暴露原发器对象的内部结构了。

  • 热点排行