电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

备忘录模式(Memento Pattern)


发布日期:2022/2/6
 

描述

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

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

例子

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

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

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

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

DataConverter(发起者)

DataConverter类(图和Listing)是数据转化过程的实现

Figure : DataConverter Class?The Originator

Listing : DataConverter Class

public class DataConverter {

public static final String DATA_FILE = Datatxt;

public static final String OUTPUT_FILE = SQLtxt;

private long ID = ;

public Memento createMemento() {

return (new Memento(ID));

}

public void setMemento(Memento memento) {

if (memento != null)

ID = mementogetID();

}

public long getLastProcessedID() {

return ID;

}

public void setLastProcessedID(long lastID) {

ID = lastID;

}

public boolean process() {

boolean success = true;

String inputLine = ;

long currID = ;

try {

File inFile = new File(DATA_FILE);

BufferedReader br = new BufferedReader(

new InputStreamReader(

new FileInputStream(inFile)));

long lastID = getLastProcessedID();

while ((inputLine = brreadLine()) != null) {

StringTokenizer st =

new StringTokenizer(inputLine );

String strID = stnextToken();

currID = new Long(strID)longValue();

if (lastID < currID) {

Customer c =

new Customer(strID stnextToken()

stnextToken() stnextToken());

if (!(cisValid())) {

success = false;

break;

}

ID = new Long(strID)longValue();

FileUtil util = new FileUtil();

utilwriteToFile(OUTPUT_FILE cgetSQL()

true true);

}

}

brclose();

}//Try

catch (Exception ex) {

Systemoutprintln( An error has occurred +

exgetMessage());

Systemexit();

}

if (success == false) {

Systemoutprintln(An error has occurred at ID= +

currID);

Systemoutprintln(Data Record= + inputLine);

return false;

}

return true;

}

class Memento implements javaioSerializable {

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类需要实现javaioSerializable接口以表明自己是一个可序列化(Serializable)的类

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

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

实现javaioSerializable接口

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

process

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

createMemento

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

setMemento

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

DCClient (Client)

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

Listing : DCClient Class

public class DCClient {

public static void main(String[] args) {

MementoHandler objMementoHandler = new MementoHandler();

DataConverter objConverter = new DataConverter();

objConvertersetMemento(objMementoHandlergetMemento());

if (!(objConverterprocess())) {

Systemoutprintln(Description: Invalid data +

Process Stopped);

Systemoutprintln(Please correct the Data and +

Run the Application Again);

objMementoHandlersetMemento(

objConvertercreateMemento());

}

}

}

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

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

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

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

MementoHandler

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

Listing : MementoHandler Class

public class MementoHandler {

public static final String ID_FILE = IDtxt;

private DataConverterMemento objMemento = null;

public DataConverterMemento getMemento() {

ObjectInputStream objStream = null;

FileUtil util = new FileUtil();

if (utilisFileExists(ID_FILE)) {

//read the object from the file

try {

objStream = new ObjectInputStream(

new FileInputStream(new File(ID_FILE)));

objMemento = (DataConverterMemento)

objStreamreadObject();

objStreamclose();

} catch (Exception e) {

Systemoutprintln(Error Reading Memento);

Systemexit();

}

//delete the old memento

utildeleteFile(ID_FILE);

}

return objMemento;

}

public void setMemento(DataConverterMemento memento) {

ObjectOutputStream objStream = null;

//write the object to the file

try {

objStream = new ObjectOutputStream(

new FileOutputStream(new File(ID_FILE)));

objStreamwriteObject(memento);

objStreamclose();

} catch (Exception e) {

Systemoutprintln(Error Writing Memento);

Systemexit();

}

}

}//end of class

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

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

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

Figure : Data Conversion Application?Class Association

Figure shows the application message flow

Figure : Application Message Flow

例子(自己找的)

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

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

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

public class WindowsSystem {

private String state;

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

return new Memento(state);

}

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

thisstate=mementogetState();

}

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

return thisstate;

}

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

thisstate=state;

Systemoutprintln(当前系统处于+thisstate);

}

}

再定义备份(Memento)类

public class Memento {

private String state;

public Memento(String state) {//备份

thisstate=state;

}

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

return thisstate;

}

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

thisstate=state;

}

}

定义用户(User)类

public class User {

private Memento memento;

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

return mento;

}

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

mento=memento;

}

}

编写测试类

public class Test {

public static void main(String args[]) {

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

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

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

usersaveMemento(WinxpcreateMemento()); //用户对系统进行备份Winxp系统要产生备份文件

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

WinxprestoreMemento(userretrieveMemento()); //用户发恢复命令系统进行恢复

Systemoutprintln(当前系统处于+WinxpgetState());

}

}

说明

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

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

CMemento模式所涉及的角色有三个备忘录角色发起人角色和负责人角色

备忘录角色的作用

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

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

发起人角色的作用

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

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

负责人角色的作用

) 负责保存备忘录对象

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

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

上一篇:关于线程的停止问题

下一篇:采用敏捷方法进行用户界面开发