分享
 
 
 

备忘录模式(Memento Pattern)

王朝other·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

描述:

对象的状态可以定义为在特定的时间点对象的属性值。备忘录模式(Memento Pattern)应用于保存和跟踪对象的状态,以便于必要的时候可以把对象恢复到以前的状态。它很像恢复操作。备忘录模式(Memento Pattern)可以在不暴露对象的内部结构的情况下完成这样的功能。需要获取以前状态的对象就是指发起者(Originator)。当客户需要保存发起者的状态时,客户需要发起者的当前状态。发起者存贮所有保持它状态的属性到一个独立的对象,这个对象就是备忘录(纪念、记忆)Memento,把备忘录(Memento)对象返回给客户。备忘录(Memento)对象可以看作在给定的时间点包含另一个对象内部状态的对象。备忘录(Memento)对象必须向除了发起者以外的所有对象隐藏发起者变量的值。当发起者答应备忘录(Memento)对象访问它的内部状态时,备忘录(Memento)对象应该被设计为对其他对象采取访问限制的对象。

当客户需要把发起者的状态恢复到以前的状态时,它只是简单的把备忘录(Memento)对象返回给发起者。发起者使用包含在备忘录(Memento)对象中的状态信息,恢复自己到备忘录(Memento)对象中保存的状态。

例子:

数据转化(Data conversion)总是那些涉及到从遗留系统转化到应用新技术的系统不可缺少的一部分。让我们假定一个需要把客户数据从文本文件移植到关系型数据库中的类似应用程序。在将客户数据发送给数据库以前,要对客户纪录进行验证。

现实中,客户纪录需要包括很多属性,但是为了简单,让我们假定每一个客户纪录只有三个属性??first name、last name和credit card number。验证过程也很简单,只要last name不为空而且credit card number(信用卡号)仅有0-9的数字组成。当发现一个无效的客户记录时,验证过程需要停止、提示用户修正数据并重新开始。在这个时间点上,数据转化(Data conversion)过程的状态需要保存在一个备忘录(Memento)对象内部。当用户重新开始验证过程时,数据装化过程从保存在备忘录(Memento)对象中的状态开始,验证过程从它停止的地方恢复,而不是从原数据起点重新开始。通常,备忘录(Memento)对象既可以保存在内存中也可以保存在持久介质上。在这个应用中,当应用被打断以后,状态需要保存,而且当应用再次运行的时候需要恢复。因此,在这种情况下,不适于把备忘录(Memento)对象保存在内存中,而是需要保存在持久介质上。

不是直接把合法的客户纪录插入到关系数据库中,应用程序而是生成一个由SQL插入语句组成的文本文件,执行这些SQL语句可以把数据插入到数据库中。

让我们为这个验证过程设计不同的组件。

DataConverter(发起者)

DataConverter类(图32.1和Listing32.1)是数据转化过程的实现。

Figure 32.1: DataConverter Class?The Originator

Listing 32.1: DataConverter Class

public class DataConverter {

public static final String DATA_FILE = "Data.txt";

public static final String OUTPUT_FILE = "SQL.txt";

PRivate long ID = 0;

public Memento createMemento() {

return (new Memento(ID));

}

public void setMemento(Memento memento) {

if (memento != null)

ID = memento.getID();

}

public long getLastProcessedID() {

return ID;

}

public void setLastProcessedID(long lastID) {

ID = lastID;

}

public boolean process() {

boolean sUCcess = true;

String inputLine = "";

long currID = 0;

try {

File inFile = new File(DATA_FILE);

BufferedReader br = new BufferedReader(

new InputStreamReader(

new FileInputStream(inFile)));

long lastID = getLastProcessedID();

while ((inputLine = br.readLine()) != null) {

StringTokenizer st =

new StringTokenizer(inputLine, ",");

String strID = st.nextToken();

currID = new Long(strID).longValue();

if (lastID < currID) {

Customer c =

new Customer(strID, st.nextToken(),

st.nextToken(), st.nextToken());

if (!(c.isValid())) {

success = false;

break;

}

ID = new Long(strID).longValue();

FileUtil util = new FileUtil();

util.writeToFile(OUTPUT_FILE, c.getSQL(),

true, true);

}

}

br.close();

}//Try

catch (Exception ex) {

System.out.println(" An error has occurred " +

ex.getMessage());

System.exit(1);

}

if (success == false) {

System.out.println("An error has occurred at ID=" +

currID);

System.out.println("Data Record=" + inputLine);

return false;

}

return true;

}

class Memento implements java.io.Serializable {

private long lastProcessedID;

private Memento(long ID) {

lastProcessedID = ID;

}

private long getID() {

return lastProcessedID;

}

}//end of class

}//end of class

ID

实例变量ID组成了DataConverter的状态,它代表了最后一个被成功处理的客户纪录的客户ID。

Memento

Memento定义为DataConverter的一个内部类,Memento将它的构造函数和其他方法定义为私有。

在Java中,一个类可以访问它内部类的私有成员。

DataConverter可以访问这些方法,但是其他的对象不可以访问。因为,当应用结束的时候,DataConverter的状态需要被保存。Memento对象需要被序列化(serialize)到一个文件中。因此,Memento类需要实现java.io.Serializable接口,以表明自己是一个可序列化(Serializable)的类。

在JAVA中,一个序列化的类必须:

使用transient 要害字明确指出不需要序列化的属性。

实现java.io.Serializable接口

可以访问它的第一个非序列化夫类的零参数的构造函数。

process

process方法读取元数据文件,通过Customer helper类验证客户数据。对于每一个有效的客户纪录,相应的SQL插入语句被写入到输出文件中。当碰到无效客户纪录时,数据转化过程停止。

createMemento

如方法名字,这个方法负责创建Memento对象,它把DataConverter对象的当前状态保存到一个Memento实例内,并放回它。

setMemento

取出输入的Memento对象的状态信息,重新设置DataConverter的状态到此状态。

DCClient (Client)

客户DCClient(Listing 32.2)首先初始化DataConverter,调用DataConverter实例的process方法开始数据转化过程。假如process方法在处理原数据文件期间碰到无效的客户数据,它会调用DataConverter实例的createMemento方法捕捉当前状态。createMemento方法返回一个Memento对象。客户DCClient使用MementoHandler对象负责序列化Memento实例到一个文件。

Listing 32.2: DCClient Class

public class DCClient {

public static void main(String[] args) {

MementoHandler objMementoHandler = new MementoHandler();

DataConverter objConverter = new DataConverter();

objConverter.setMemento(objMementoHandler.getMemento());

if (!(objConverter.process())) {

System.out.println("Description: Invalid data - " +

"Process Stopped");

System.out.println("Please correct the Data and " +

"Run the application Again");

objMementoHandler.setMemento(

objConverter.createMemento());

}

}

}

一旦数据被校正,客户DCClient就会再次运行。

客户DCClient调用MementoHandler 上的getMemento 方法请求它保存Memento对象。

MementoHandler 从文件中反序列化以前的Memento对象,并把它放回给客户。

客户把它作为DataConverter 的set-Memento方法的参数传递给DataConverter 。DataConverter使自己返回到保存在Memento对象中的状态。从原来停止的地方恢复数据转化过程。

MementoHandler

The MementoHandler (Listing 32.3) 包含了Memento 对象的一个引用。客户DCClient把一个Memento的实例传递给它。

Listing 32.3: MementoHandler Class

public class MementoHandler {

public static final String ID_FILE = "ID.txt";

private DataConverter.Memento objMemento = null;

public DataConverter.Memento getMemento() {

ObjectInputStream objStream = null;

FileUtil util = new FileUtil();

if (util.isFileExists(ID_FILE)) {

//read the object from the file

try {

objStream = new ObjectInputStream(

new FileInputStream(new File(ID_FILE)));

objMemento = (DataConverter.Memento)

objStream.readObject();

objStream.close();

} catch (Exception e) {

System.out.println("Error Reading Memento");

System.exit(1);

}

//delete the old memento

util.deleteFile(ID_FILE);

}

return objMemento;

}

public void setMemento(DataConverter.Memento memento) {

ObjectOutputStream objStream = null;

//write the object to the file

try {

objStream = new ObjectOutputStream(

new FileOutputStream(new File(ID_FILE)));

objStream.writeObject(memento);

objStream.close();

} catch (Exception e) {

System.out.println("Error Writing Memento");

System.exit(1);

}

}

}//end of class

如上面介绍的,任何时候数据转化过程没有验证完全部的原数据文件,客户要捕捉DataConverter的状态到一个Memento中,并停止应用程序。为了使这个Memento在下次运行的时候有效,它必须被保存到持久介质上,这就涉及到了对象的序列化。假如在下次运行的时候,DataConverter要返回到它原来的状态,这个Memento对象必须被重构,这又涉及到了对象的反序列化。这些细节由MementoHandler类来处理,使得所有客户(DataConverter和Memento对象)免于处理这些细节。

这样也是的改变Memento的存储方式变得很轻易。例如,Memento需要保存到数据库中,而不是文件中时,只要修改MementoHandler就可以了,不需要修改任何客户类的实现。

图32.2显示了在数据转化这个例子中,不同对象之间的关联关系。

Figure 32.2: Data Conversion Application?Class Association

Figure 32.3 shows the application message flow.

Figure 32.3: Application Message Flow

例子2(自己找的)

经常使用计算机的人恐怕对系统备份(Memento)不会生疏,当你的Windows系统运行正常时,对它进行备份,当系统运行有问题时,就可以调用备份快速的将系统恢复,这样就可以大量节省重新装系统的痛苦,非凡是当你缺少某一驱动,或在装系统是出现一些怪问题时,犹为痛苦。我想有过这种经历的人应该很了解吧,呵呵!

好了,下面让我们看看这个过程该如何实现吧:

1、我们先定义Windows系统(WindowsSystem)类:

public class WindowsSystem {

private String state;

public Memento createMemento() {//创建备份,保存当前状态

return new Memento(state);

}

public void restoreMemento(Memento memento){ //从备份中恢复系统

this.state=memento.getState();

}

public String getState(){//获得状态

return this.state;

}

public void setState(String state){//设置状态

this.state=state;

System.out.println(当前系统处于+this.state);

}

}

2、再定义备份(Memento)类:

public class Memento {

private String state;

public Memento(String state) {//备份

this.state=state;

}

public String getState(){ //获得状态

return this.state;

}

public void setState(String state){//设置状态

this.state=state;

}

}

3、定义用户(User)类:

public class User {

private Memento memento;

public Memento retrieveMemento() {//恢复系统

return this.memento;

}

public void saveMemento(Memento memento){//保存系统

this.memento=memento;

}

}

4、编写测试类:

public class Test {

public static void main(String args[]) {

WindowsSystem WinXP = new WindowsSystem(); //Winxp系统

User user = new User(); //某一用户

Winxp.setState(好的状态); //Winxp处于好的运行状态

user.saveMemento(Winxp.createMemento()); //用户对系统进行备份,Winxp系统要产生备份文件

Winxp.setState(坏的状态); //Winxp处于不好的运行状态

Winxp.restoreMemento(user.retrieveMemento()); //用户发恢复命令,系统进行恢复

System.out.println(当前系统处于+Winxp.getState());

}

}

5、说明:

A:定义:Memento对象是一个保存另外一个对象内部状态拷贝的对象,这样以后就可以将该对象恢复到原先保存的状态。

B:Memento模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

C:Memento模式所涉及的角色有三个,备忘录角色、发起人角色和负责人角色。

备忘录角色的作用:

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

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

发起人角色的作用:

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

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

负责人角色的作用:

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

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

D:在本例中,备份(Memento)类是备忘录角色、Windows系统(WindowsSystem)类是发起人角色、用户(User)类是负责人角色。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有