考虑这样一个仿真应用,功能是:模拟运行针对某个具体问题的多个解决方案,记录运行过程的各类数据,在模拟运行完成事后,好对这多个解决方案进行比较和评价,从而选定最优的解决方案。html
这种仿真系统,在不少领域都有应用,好比:工做流系统,对同一问题制定多个流程,而后经过仿真运行,最后来肯定最优的流程作为解决方案;在工业设计和制造领域,仿真系统的应用就更普遍了。java
因为都是解决同一个具体的问题,这多个解决方案并非彻底不同的,假定它们的前半部分运行是彻底同样的,只是在后半部分采用了不一样的解决方案,后半部分须要使用前半部分运行所产生的数据。设计模式
因为要模拟运行多个解决方案,并且最后要根据运行结果来进行评价,这就意味着每一个方案的后半部分的初始数据应该是同样,也就是说在运行每一个方案后半部分以前,要保证数据都是由前半部分运行所产生的数据,固然,我们这里并不具体的去深刻到底有哪些解决方案,也不去深刻到底有哪些状态数据,这里只是示意一下。数组
那么,这样的系统该如何实现呢?尤为是每一个方案运行须要的初始数据应该同样,要如何来保证呢?缓存
要保证初始数据的一致,实现思路也很简单:数据结构
首先模拟运行流程第一个阶段,获得后阶段各个方案运行须要的数据,并把数据保存下来,以备后用ide
每次在模拟运行某一个方案以前,用保存的数据去从新设置模拟运行流程的对象,这样运行后面不一样的方案时,对于这些方案,初始数据就是同样的了学习
根据上面的思路,来写出仿真运行的示意代码,示例代码以下:测试
/** * 模拟运行流程A,只是一个示意,代指某个具体流程 */ public class FlowAMock { /** * 流程名称,不须要外部存储的状态数据 */ private String flowName; /** * 示意,代指某个中间结果,须要外部存储的状态数据 */ private int tempResult; /** * 示意,代指某个中间结果,须要外部存储的状态数据 */ private String tempState; /** * 构造方法,传入流程名称 * @param flowName 流程名称 */ public FlowAMock(String flowName){ this.flowName = flowName; } public String getTempState() { return tempState; } public void setTempState(String tempState) { this.tempState = tempState; } public int getTempResult() { return tempResult; } public void setTempResult(int tempResult) { this.tempResult = tempResult; } /** * 示意,运行流程的第一个阶段 */ 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; } }
看看如何使用这个模拟流程的对象,写个客户端来测试一下。示例代码以下:this
public class Client { public static void main(String[] args) { // 建立模拟运行流程的对象 FlowAMock mock = new FlowAMock("TestFlow"); //运行流 程的第一个阶段 mock.runPhaseOne(); //获得第一个阶段运行所产生的数据,后面要用 int tempResult = mock.getTempResult(); String tempState = mock.getTempState(); //按照方案一来运行流程后半部分 mock.schema1(); //把第一个阶段运行所产生的数据从新设置回去 mock.setTempResult(tempResult); mock.setTempState(tempState); //按照方案二来运行流程后半部分 mock.schema2(); } }
运行结果以下:
PhaseOne,Schema1 : now run 3 PhaseOne,Schema2 : now run 3
仔细看,上面结果中框住的部分,是同样的值,这说明运行时,它们的初始数据是同样的,基本知足了功能要求。
看起来实现很简单,是吧,想想有没有什么问题呢?
上面的实现有一个不太好的地方,那就是数据是一个一个零散着在外部存放的,若是须要外部存放的数据多了,会显得很杂乱。这个好解决,只须要定义一个数据对象来封装这些须要外部存放的数据就能够了,上面那样作是故意的,好提醒你们这个问题。这个就不去示例了。
还有一个严重的问题,那就是:为了把运行期间的数据放到外部存储起来,模拟流程的对象被迫把内部数据结构开放出来,这暴露了对象的实现细节,并且也破坏了对象的封装性。原本这些数据只是模拟流程的对象内部数据,应该是不对外的。
那么究竟如何实现这样的功能会比较好呢?
备忘录模式定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象以外保存这个状态。这样之后就可将该对象回复到原先保存的状态。
Memento:
备忘录。主要用来存储原发器对象的内部状态,可是具体须要存储哪些数据是由原发器对象来决定的。另外备忘录应该只能由原发器对象来访问它内部的数据,原发器外部的对象不该该能访问到备忘录对象的内部数据。
Originator:
原发器。使用备忘录来保存某个时刻原发器自身的状态,也可使用备忘录来恢复内部状态。
Caretaker:
备忘录管理者,或者称为备忘录负责人。主要负责保存备忘录对象,可是不能对备忘录对象的内容进行操做或检查。
(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; } }
学习了备忘录模式的基本知识事后,来尝试一下,使用备忘录模式把前面的示例重写一下,好看看如何使用备忘录模式。
首先,那个模拟流程运行的对象,就至关于备忘录模式中的原发器;
而它要保存的数据,原来是零散的,如今作一个备忘录对象来存储这些数据,而且把这个备忘录对象实现成为内部类;
固然为了保存这个备忘录对象,仍是须要提供管理者对象的;
为了和管理者对象交互,管理者须要知道保存对象的类型,那就提供一个备忘录对象的窄接口来供管理者使用,至关于标识了类型。
(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
好好体会一下上面的示例,因为备忘录对象是一个私有的内部类,外面只能经过备忘录对象的窄接口来获取备忘录对象,而这个接口没有任何方法,仅仅起到了一个标识对象类型的做用,从而保证内部的数据不会被外部获取或是操做,保证了原发器对象的封装性,也就再也不暴露原发器对象的内部结构了。
在命令模式中,讲到了可撤销的操做,在那里讲到:有两种基本的思路来实现可撤销的操做,一种是补偿式或者反操做式:好比被撤销的操做是加的功能,那撤消的实现就变成减的功能;同理被撤销的操做是打开的功能,那么撤销的实现就变成关闭的功能。
另一种方式是存储恢复式,意思就是把操做前的状态记录下来,而后要撤销操做的时候就直接恢复回去就能够了。
这里就该来实现第二种方式,就是存储恢复式,为了让你们更好的理解可撤销操做的功能,仍是用原来的那个例子,对比学习会比较清楚。
这也至关因而命令模式和备忘录模式结合的一个例子,并且因为命令列表的存在,对应保存的备忘录对象也是多个。
1:范例需求
考虑一个计算器的功能,最简单的那种,只能实现加减法运算,如今要让这个计算器支持可撤销的操做。
2:存储恢复式的解决方案
存储恢复式的实现,可使用备忘录模式,大体实现的思路以下:
把原来的运算类,就是那个Operation类,看成原发器,原来的内部状态result,就只提供一个getter方法,来让外部获取运算的结果
在这个原发器里面,实现一个私有的备忘录对象
把原来的计算器类,就是Calculator类,看成管理者,把命令对应的备忘录对象保存在这里。当须要撤销操做的时候,就把相应的备忘录对象设置回到原发器去,恢复原发器的状态
一块儿来看看具体的实现,会更清楚。
(1)定义备忘录对象的窄接口,示例代码以下:
public interface Memento { //空的}
(2)定义命令的接口,有几点修改:
修改原来的undo方法,传入备忘录对象
添加一个redo方法,传入备忘录对象
添加一个createMemento的方法,获取须要被保存的备忘录对象
示例代码以下:
/** * 定义一个命令的接口 */ public interface Command { /** * 执行命令 */ public void execute(); /** * 撤销命令,恢复到备忘录对象记录的状态 * @param m 备忘录对象 */ public void undo(Memento m); /** * 重作命令,恢复到备忘录对象记录的状态 * @param m 备忘录对象 */ public void redo(Memento m); /** * 建立保存原发器对象的状态的备忘录对象 * @return 建立好的备忘录对象 */ public Memento createMemento(); }
(3)再来定义操做运算的接口,至关于计算器类这个原发器对外提供的接口,它须要作以下的调整:
去掉原有的setResult方法,内部状态,不容许外部操做
添加一个createMemento的方法,获取须要保存的备忘录对象
添加一个setMemento的方法,来从新设置原发器对象的状态
示例代码以下:
/** * 操做运算的接口 */ public interface OperationApi { /** * 获取计算完成后的结果 * @return 计算完成后的结果 */ public int getResult(); /** * 执行加法 * @param num 须要加的数 */ public void add(int num); /** * 执行减法 * @param num 须要减的数 */ public void substract(int num); /** * 建立保存原发器对象的状态的备忘录对象 * @return 建立好的备忘录对象 */ public Memento createMemento(); /** * 从新设置原发器对象的状态,让其回到备忘录对象记录的状态 * @param memento 记录有原发器状态的备忘录对象 */ public void setMemento(Memento memento); }
(4)因为如今撤销和恢复操做是经过使用备忘录对象,直接来恢复原发器的状态,所以就再也不须要按照操做类型来区分了,对于全部的命令实现,它们的撤销和重作都是同样的。原来的实现是要区分的,若是是撤销加的操做,那就是减,而撤销减的操做,那就是加。如今就不区分了,统一使用备忘录对象来恢复。
所以,实现一个全部命令的公共对象,在里面把公共功能都实现了,这样每一个命令在实现的时候就简单了。顺便把设置持有者的公共实现也放到这个公共对象里面来,这样各个命令对象就不用再实现这个方法了,示例代码以下:
/** * 命令对象的公共对象,实现各个命令对象的公共方法 */ public abstract class AbstractCommand implements Command{ /** * 具体的功能实现,这里无论 */ public abstract void execute(); /** * 持有真正的命令实现者对象 */ protected OperationApi operation = null; public void setOperation(OperationApi operation) { this.operation = operation; } public Memento createMemento() { return this.operation.createMemento(); } public void redo(Memento m) { this.operation.setMemento(m); } public void undo(Memento m) { this.operation.setMemento(m); } }
(5)有了公共的命令实现对象,各个具体命令的实现就简单了,实现加法命令的对象实现,再也不直接实现Command接口了,而是继承命令的公共对象,这样只须要实现跟本身命令相关的业务方法就行了,示例代码以下:
public class AddCommand extends AbstractCommand{ private int opeNum; public AddCommand(int opeNum){ this.opeNum = opeNum; } public void execute() { this.operation.add(opeNum); } }
看看减法命令的实现,跟加法命令的实现差很少,示例代码以下:
public class SubstractCommand extends AbstractCommand{ private int opeNum; public SubstractCommand(int opeNum){ this.opeNum = opeNum; } public void execute() { this.operation.substract(opeNum); } }
(6)接下来看看运算类的实现,至关因而原发器对象,它的实现有以下改变:
再也不提供setResult方法,内部状态,不容许外部来操做
添加了createMemento和setMemento方法的实现
添加实现了一个私有的备忘录对象
示例代码以下:
/** * 运算类,真正实现加减法运算 */ public class Operation implements OperationApi{ /** * 记录运算的结果 */ private int result; public int getResult() { return result; } public void add(int num){ result += num; } public void substract(int num){ result -= num; } /** * 结果保存到备忘录实例中 */ public Memento createMemento() { MementoImpl m = new MementoImpl(result); return m; } /** * 使用备忘录中结果重写赋值 */ public void setMemento(Memento memento) { MementoImpl m = (MementoImpl)memento; this.result = m.getResult(); } /** * 备忘录对象 */ private static class MementoImpl implements Memento{ private int result = 0; public MementoImpl(int result){ this.result = result; } public int getResult() { return result; } } }
(7)接下来该看看如何具体的使用备忘录对象来实现撤销操做和重作操做了。一样在计算器类里面实现,这个时候,计算器类就至关因而备忘录模式管理者对象。
实现思路:因为对于每一个命令对象,撤销和重作的状态是不同的,撤销是回到命令操做前的状态,而重作是回到命令操做后的状态,所以对每个命令,使用一个备忘录对象的数组来记录对应的状态。
这些备忘录对象是跟命令对象相对应的,所以也跟命令历史记录同样,设立相应的历史记录,它的顺序跟命令彻底对应起来。在操做命令的历史记录的同时,对应操做相应的备忘录对象记录。
示例代码以下:
/** * 计算器类,计算器上有加法按钮、减法按钮,还有撤销和恢复的按钮 */ public class Calculator { /** * 命令的操做的历史记录,在撤销时候用 */ private List<Command> undoCmds = new ArrayList<Command>(); /** * 命令被撤销的历史记录,在恢复时候用 */ private List<Command> redoCmds = new ArrayList<Command>(); /** * 命令操做对应的备忘录对象的历史记录,在撤销时候用, * 数组有两个元素,第一个是命令执行前的状态,第二个是命令执行后的状态 */ private List<Memento[]> undoMementos = new ArrayList<Memento[]>(); /** * 被撤销命令对应的备忘录对象的历史记录,在恢复时候用, * 数组有两个元素,第一个是命令执行前的状态,第二个是命令执行后的状态 */ private List<Memento[]> redoMementos = new ArrayList<Memento[]>(); private Command addCmd = null; private Command substractCmd = null; public void setAddCmd(Command addCmd) { this.addCmd = addCmd; } public void setSubstractCmd(Command substractCmd) { this.substractCmd = substractCmd; } public void addPressed(){ //获取对应的备忘录对象,并保存在相应的历史记录里面 Memento m1 = this.addCmd.createMemento(); //执行命令 this.addCmd.execute(); //把操做记录到历史记录里面 undoCmds.add(this.addCmd); //获取执行命令后的备忘录对象 Memento m2 = this.addCmd.createMemento(); //设置到撤销的历史记录里面 this.undoMementos.add(new Memento[]{m1,m2}); } public void substractPressed(){ //获取对应的备忘录对象,并保存在相应的历史记录里面 Memento m1 = this.substractCmd.createMemento(); //执行命令 this.substractCmd.execute(); //把操做记录到历史记录里面 undoCmds.add(this.substractCmd); //获取执行命令后的备忘录对象 Memento m2 = this.substractCmd.createMemento(); //设置到撤销的历史记录里面 this.undoMementos.add(new Memento[]{m1,m2}); } public void undoPressed(){ if(undoCmds.size()>0){ //取出最后一个命令来撤销 Command cmd = undoCmds.get(undoCmds.size()-1); //获取对应的备忘录对象 Memento[] ms = undoMementos.get(undoCmds.size()-1); //撤销 cmd.undo(ms[0]); //若是还有恢复的功能,那就把这个命令记录到恢复的历史记录里面 redoCmds.add(cmd); //把相应的备忘录对象也添加过去 redoMementos.add(ms); //而后把最后一个命令删除掉, undoCmds.remove(cmd); //把相应的备忘录对象也删除掉 undoMementos.remove(ms); }else{ System.out.println("很抱歉,没有可撤销的命令"); } } public void redoPressed(){ if(redoCmds.size()>0){ //取出最后一个命令来重作 Command cmd = redoCmds.get(redoCmds.size()-1); //获取对应的备忘录对象 Memento[] ms = redoMementos.get(redoCmds.size()-1); //重作 cmd.redo(ms[1]); //把这个命令记录到可撤销的历史记录里面 undoCmds.add(cmd); //把相应的备忘录对象也添加过去 undoMementos.add(ms); //而后把最后一个命令删除掉 redoCmds.remove(cmd); //把相应的备忘录对象也删除掉 redoMementos.remove(ms); }else{ System.out.println("很抱歉,没有可恢复的命令"); } } }
(8)客户端跟之前的实现没有什么变化,示例代码以下:
public class Client { public static void main(String[] args) { //1:组装命令和接收者 //建立接收者 OperationApi operation = new Operation(); //建立命令 AddCommand addCmd = new AddCommand(5); SubstractCommand substractCmd = new SubstractCommand(3); //组装命令和接收者 addCmd.setOperation(operation); substractCmd.setOperation(operation); //2:把命令设置到持有者,就是计算器里面 Calculator calculator = new Calculator(); calculator.setAddCmd(addCmd); calculator.setSubstractCmd(substractCmd); //3:模拟按下按钮,测试一下 calculator.addPressed(); System.out.println("一次加法运算后的结果为:" +operation.getResult()); calculator.substractPressed(); System.out.println("一次减法运算后的结果为:" +operation.getResult()); //测试撤消 calculator.undoPressed(); System.out.println("撤销一次后的结果为:" +operation.getResult()); calculator.undoPressed(); System.out.println("再撤销一次后的结果为:" +operation.getResult()); //测试恢复 calculator.redoPressed(); System.out.println("恢复操做一次后的结果为:" +operation.getResult()); calculator.redoPressed(); System.out.println("再恢复操做一次后的结果为:" +operation.getResult()); } }
运行结果,示例以下:示例代码以下:
一次加法运算后的结果为:5 一次减法运算后的结果为:2 撤销一次后的结果为:5 再撤销一次后的结果为:0 恢复操做一次后的结果为:5 再恢复操做一次后的结果为:2
跟前面采用补偿式或者反操做式获得的结果是同样的。好好体会一下,对比两种实现方式,看看都是怎么实现的。顺便也体会一下命令模式和备忘录模式是如何结合起来实现功能的。
更好的封装性;
备忘录模式经过使用备忘录对象,来封装原发器对象的内部状态,虽然这个对象是保存在原发器对象的外部,可是因为备忘录对象的窄接口并不提供任何方法,这样有效的保证了对原发器对象内部状态的封装,不把原发器对象的内部实现细节暴露给外部。
简化了原发器
备忘录模式中,备忘录对象被保存到原发器对象以外,让客户来管理他们请求的状态,从而让原发器对象获得简化。
窄接口和宽接口
备忘录模式,经过引入窄接口和宽接口,使得不一样的地方,对备忘录对象的访问是不同的。窄接口保证了只有原发器才能够访问备忘录对象的状态。
可能会致使高开销
备忘录模式基本的功能,就是对备忘录对象的存储和恢复,它的基本实现方式就是缓存备忘录对象。这样一来,若是须要缓存的数据量很大,或者是特别频繁的建立备忘录对象,开销是很大的。
备忘录模式和命令模式
这两个模式能够组合使用。
命令模式实现中,在实现命令的撤销和重作的时候,可使用备忘录模式,在命令操做的时候记录下操做先后的状态,而后在命令撤销和重作的时候,直接使用相应的备忘录对象来恢复状态就能够了。
在这种撤销的执行顺序和重作执行顺序可控的状况下,备忘录对象还能够采用增量式记录的方式,能够减小缓存的数据量。
备忘录模式和原型模式
这两个模式能够组合使用。
在原发器对象建立备忘录对象的时候,若是原发器对象中所有或者大部分的状态都须要保存,一个简洁的方式就是直接克隆一个原发器对象。也就是说,这个时候备忘录对象里面存放的是一个原发器对象的实例
转载至:http://sishuok.com/forum/blogPost/list/5633.html
cc老师的设计模式是我目前看过最详细最有实践的教程。