Java资源网

| JAVA基础 | 环境配置 | JDBC | 线程技术 | Socket编程 | JavaMail | JAVA与XML | 设计模式 | 技术新闻 | Java认证 | 程序人生 软件下载
| JSP&Servlet | Spring | Struts | Hibernate | JBuilder | Eclipse | WebService | EJB技术 | J2ME开发 | 应用服务器 | JXTA | Ajax
Articles search文章搜索
   关键字:
   类 别:
       
New download 最新下载
· [组件]HTML Parser 1.5
· [教程]WebSphere Studio应用教程
· [组件]JDom 1.0
· [工具]Junit3.8.1
· [教程]EJB编程及J2EE系统架构和设计
· [教程]EJB教程
· [教程]J2EE Tutorial中文版
· [教程]Java编程思想2(英文)
· [教程]java编程思想(完整版)
· [教程]Java网络编程
New articles 最新文章
· 设计移动 Web 服务
· 解析XML的时候完全忽略DTD
· 理解XML Schema XML Schema 初步
· 标签库的深入研究
· 提升JSP应用程序的七大绝招
· 如何使用JDOM对XML文件进行操作
· 处理XML字符串中特殊字符
· 利用Digester把XML转换成为Java对象
· 使用WebService 和RMI远程协作
· 使用Axis开发Web Service程序
Articles top 热门文章
· Eclipse基础--plugin插件安装(6644)
· eclipse+tomcat+lomboz的安装配置说明(4774)
· Java程序员就业前景(4584)
· Windows下JAVA环境变量的设置祥解(3788)
· Tomcat下JSP、Servlet和JavaBean环境的配置(3716)
· 使用links方式安装Eclipse插件(3698)
· 一个老程序员的心理话(3533)
· linux下jdk的安装与配置(3459)
· 初学者入门:Structs中基本配置入门(3334)
· Eclipse 运行命令行参数大全(3084)
您的位置:首页>>设计模式>>备忘录模式(Mementonbsp;Pattern)
备忘录模式(Mementonbsp;Pattern)
2007-04-13   来源:www.javaresearch.org  作者:未知

第32章 备忘录模式(Memento Pattern)


    

描述:


    对象的状态可以定义为在特定的时间点对象的属性值。备忘录模式(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 
  1. public class DataConverter { 
  2.   public static final String DATA_FILE = "Data.txt"
  3.   public static final String OUTPUT_FILE = "SQL.txt"
  4.   private long ID = 0; 
  5.   public Memento createMemento() { 
  6.     return (new Memento(ID)); 
  7.   } 
  8.   public void setMemento(Memento memento) { 
  9.     if (memento != null
  10.      ID = memento.getID(); 
  11.   } 
  12.   public long getLastProcessedID() { 
  13.     return ID; 
  14.   } 
  15.   public void setLastProcessedID(long lastID) { 
  16.     ID = lastID; 
  17.   } 
  18.    public boolean process() { 
  19.     boolean success = true
  20.     String inputLine = ""
  21.     long currID = 0; 
  22.     try { 
  23.      File inFile = new File(DATA_FILE); 
  24.      BufferedReader br = new BufferedReader
  25.                           new InputStreamReader
  26.                            new FileInputStream(inFile))); 
  27.      long lastID = getLastProcessedID(); 
  28.      while ((inputLine = br.readLine()) != null) { 
  29.        StringTokenizer st = 
  30.         new StringTokenizer(inputLine, ","); 
  31.        String strID = st.nextToken(); 
  32.        currID = new Long(strID).longValue(); 
  33.        if (lastID < currID) { 
  34.        Customer c = 
  35.         new Customer(strID, st.nextToken(), 
  36.                      st.nextToken(), st.nextToken()); 
  37.        if (!(c.isValid())) { 
  38.         success = false
  39.         break
  40.        } 
  41.        ID = new Long(strID).longValue(); 
  42.        FileUtil util = new FileUtil(); 
  43.        util.writeToFile(OUTPUT_FILE, c.getSQL(), 
  44.                         truetrue); 
  45.        } 
  46.      } 
  47.      br.close(); 
  48.     }//Try 
  49.     catch (Exception ex) { 
  50.      System.out.println(" An error has occurred " + 
  51.                         ex.getMessage()); 
  52.      System.exit(1); 
  53.     } 
  54.     if (success == false) { 
  55.      System.out.println("An error has occurred at ID=" + 
  56.                         currID); 
  57.      System.out.println("Data Record=" + inputLine); 
  58.      return false
  59.     } 
  60.     return true
  61.    } 
  62.    class Memento implements java.io.Serializable { 
  63.     private long lastProcessedID; 
  64.     private Memento(long ID) { 
  65.      lastProcessedID = ID; 
  66.     } 
  67.     private long getID() { 
  68.      return lastProcessedID; 
  69.     } 
  70.   }//end of class 
  71. }//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 
  1. public class DCClient { 
  2.   public static void main(String[] args) { 
  3.     MementoHandler objMementoHandler = new MementoHandler(); 
  4.     DataConverter objConverter = new DataConverter(); 
  5.     objConverter.setMemento(objMementoHandler.getMemento()); 
  6.     if (!(objConverter.process())) { 
  7.      System.out.println("Description: Invalid data - " + 
  8.                         "Process Stopped"); 
  9.      System.out.println("Please correct the Data and " + 
  10.                         "Run the Application Again"); 
  11.      objMementoHandler.setMemento( 
  12.        objConverter.createMemento()); 
  13.     } 
  14.   } 

一旦数据被校正,客户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 
  1. public class MementoHandler { 
  2.   public static final String ID_FILE = "ID.txt"
  3.   private DataConverter.Memento objMemento = null
  4.   public DataConverter.Memento getMemento() { 
  5.     ObjectInputStream objStream = null
  6.     FileUtil util = new FileUtil(); 
  7.     if (util.isFileExists(ID_FILE)) { 
  8.      //read the object from the file 
  9.      try { 
  10.        objStream = new ObjectInputStream
  11.                    new FileInputStream(new File(ID_FILE))); 
  12.        objMemento = (DataConverter.Memento) 
  13.                     objStream.readObject(); 
  14.        objStream.close(); 
  15.      } catch (Exception e) { 
  16.        System.out.println("Error Reading Memento"); 
  17.        System.exit(1); 
  18.      } 
  19.      //delete the old memento 
  20.      util.deleteFile(ID_FILE); 
  21.     } 
  22.     return objMemento; 
  23.   } 
  24.   public void setMemento(DataConverter.Memento memento) { 
  25.     ObjectOutputStream objStream = null
  26.     //write the object to the file 
  27.     try { 
  28.      objStream = new ObjectOutputStream
  29.                  new FileOutputStream(new File(ID_FILE))); 
  30.      objStream.writeObject(memento); 
  31.      objStream.close(); 
  32.     } catch (Exception e) { 
  33.      System.out.println("Error Writing Memento"); 
  34.      System.exit(1); 
  35.     } 
  36.   } 
  37. }//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)类:
 
  1. public class WindowsSystem {
  2.   private String state;
  3.   public Memento createMemento() {  //创建备份,保存当前状态
  4.     return new Memento(state);
  5.   }
  6.   public void restoreMemento(Memento memento){ //从备份中恢复系统
  7.     this.state=memento.getState();
  8.   }
  9.   public String getState(){  //获得状态
  10.     return this.state;
  11.   }
  12.   public void setState(String state){  //设置状态
  13.     this.state=state;
  14.     System.out.println(当前系统处于+this.state);
  15.   }
  16. }

2、再定义备份(Memento)类:
  1. public class Memento {
  2.   private String state;
  3.   public Memento(String state) {  //备份
  4.     this.state=state;
  5.   }
  6.   public String getState(){ //获得状态
  7.     return this.state;
  8.   }
  9.   public void setState(String state){  //设置状态
  10.     this.state=state;
  11.   }
  12. }

3、定义用户(User)类:
  1. public class User {
  2.   private Memento memento;
  3.   public Memento retrieveMemento() {  //恢复系统
  4.     return this.memento;
  5.   }
  6.   public void saveMemento(Memento memento){  //保存系统
  7.     this.memento=memento;
  8.   }
  9. }

4、编写测试类:
  1. public class Test {
  2.   public static void main(String args[]) {    
  3.     WindowsSystem Winxp = new WindowsSystem(); //Winxp系统
  4.     User user = new User();   //某一用户
  5.     Winxp.setState(好的状态);   //Winxp处于好的运行状态
  6.     user.saveMemento(Winxp.createMemento()); //用户对系统进行备份,Winxp系统要产生备份文件
  7.     Winxp.setState(坏的状态);   //Winxp处于不好的运行状态
  8.     Winxp.restoreMemento(user.retrieveMemento());   //用户发恢复命令,系统进行恢复
  9.     System.out.println(当前系统处于+Winxp.getState());
  10.   }
  11. }

5、说明:
A:定义:Memento对象是一个保存另外一个对象内部状态拷贝的对象,这样以后就可以将该对象恢复到原先保存的状态。
B:Memento模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。
C:Memento模式所涉及的角色有三个,备忘录角色、发起人角色和负责人角色。
备忘录角色的作用:
(1)       将发起人对象的内部状态存储起来,备忘录可以根据发起人对象的判断来决定存储多少发起人对象的内部状态。
(2)       备忘录可以保护其内容不被发起人对象之外的任何对象所读取。
发起人角色的作用:
(1)       创建一个含有当前内部状态的备忘录对象。
(2)       使用备忘录对象存储其内部状态。
负责人角色的作用:
(1)       负责保存备忘录对象。
(2)       不检查备忘录对象的内容。
D:在本例中,备份(Memento)类是备忘录角色、Windows系统(WindowsSystem)类是发起人角色、用户(User)类是负责人角色。 

附件:37.rar(11K) 

  --相关文章--
· 面向对象编程,我的思想 (2007-04-13)
· 面向对象的思维方式 (2007-04-13)
· 通过Javanbsp;Swing看透MVC设计模式 (2007-04-13)
· 适配器模式(Adapternbsp;Pattern) (2007-04-13)
· 追MM与Java的23种设计模式 (2007-04-13)
· 责任链模式(Chainnbsp;ofnbsp;Responsibility) (2007-04-13)

版权所有©2005-2006 JAVA资源网 渝ICP备05007591号 虚拟主机 | 关于我们 | 联系方式 | 广告业务 | 网站地图 | 友情链接