设计模式学习笔记——备忘录模式

原文:http://blog.csdn.net/hackerain/article/details/7563246

定义:

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态


备忘录模式主要是对某个对象的状态的备份,备份的主要是对象当前的属性值,即成员变量的值,成员变量可以有多个,而且可以备份同一个对象的多种不同状态,即同一个对象可以同时有多个备份,先来看最简单的情况,即一个对象只有一个成员变量:

其通用类图为:

源代码如下:

[java]  view plain copy
  1. /* 
  2.  * 单属性的备忘录模式,本类即是需要被备份的类 
  3.  */  
  4. public class Originator {  
  5.     private String state;  
  6.   
  7.     public String getState() {  
  8.         return state;  
  9.     }  
  10.   
  11.     public void setState(String state) {  
  12.         this.state = state;  
  13.     }  
  14.       
  15.     public void changeState(){  
  16.         this.state="heart hurt...";  
  17.     }  
  18.       
  19.     public Memento createMemento(){  
  20.         return new Memento(this.state);  
  21.     }  
  22.       
  23.     public void restoreMemento(Memento memento){  
  24.         this.setState(memento.getState());  
  25.     }  
  26. }  

[java]  view plain copy
  1. /* 
  2.  * 备忘录类 
  3.  */  
  4. public class Memento {  
  5.     private String state;  
  6.   
  7.     public Memento(String state) {  
  8.         this.state = state;  
  9.     }  
  10.   
  11.     public String getState() {  
  12.         return state;  
  13.     }  
  14.   
  15.     public void setState(String state) {  
  16.         this.state = state;  
  17.     }  
  18. }  
[java]  view plain copy
  1. /* 
  2.  * 备忘录管理类 
  3.  */  
  4. public class Caretaker {  
  5.     private Memento memento;  
  6.   
  7.     public Memento getMemento() {  
  8.         return memento;  
  9.     }  
  10.   
  11.     public void setMemento(Memento memento) {  
  12.         this.memento = memento;  
  13.     }  
  14. }  
[java]  view plain copy
  1. public class Client {  
  2.     public static void main(String[] args) {  
  3.           
  4.         Originator originator=new Originator();  
  5.         Caretaker caretaker=new Caretaker();  
  6.           
  7.         originator.setState("happy...");  
  8.         System.out.println(originator.getState());  
  9.           
  10.         //保存当前的状态  
  11.         caretaker.setMemento(originator.createMemento());  
  12.           
  13.         //状态改变  
  14.         originator.changeState();  
  15.         System.out.println(originator.getState());  
  16.           
  17.         //恢复状态  
  18.         originator.restoreMemento(caretaker.getMemento());  
  19.         System.out.println(originator.getState());  
  20.           
  21.     }  
  22. }  

要是遇到多属性怎么办?要是一个对象不仅仅只需要备份一次,而需要备份很多次,又该如何呢?看下面改进源码:

[java]  view plain copy
  1. /* 
  2.  * 多属性的备忘录模式 
  3.  */  
  4. public class Originator {  
  5.     private String state1;  
  6.     private String state2;  
  7.     private String state3;  
  8.       
  9.     public String getState1() {  
  10.         return state1;  
  11.     }  
  12.   
  13.     public void setState1(String state1) {  
  14.         this.state1 = state1;  
  15.     }  
  16.   
  17.     public String getState2() {  
  18.         return state2;  
  19.     }  
  20.   
  21.     public void setState2(String state2) {  
  22.         this.state2 = state2;  
  23.     }  
  24.   
  25.     public String getState3() {  
  26.         return state3;  
  27.     }  
  28.   
  29.     public void setState3(String state3) {  
  30.         this.state3 = state3;  
  31.     }  
  32.   
  33.     //将本对象的各个属性以HashMap类型保存到Memento对象中  
  34.     public Memento createMemento(){  
  35.         return new Memento(BeanUtils.beanToHash(this));  
  36.     }  
  37.       
  38.     //从Memento对象中取出保存的对象的属性状态,并赋值给本对象的各个属性  
  39.     public void restoreMemento(Memento memento){  
  40.         BeanUtils.hashToBean(this, memento.getStateMap());  
  41.     }  
  42.       
  43.     public String toString(){  
  44.         return this.state1+" "+this.state2+" "+this.state3;  
  45.     }  
  46. }  
[java]  view plain copy
  1. //Memento的成员变量为一个HashMap类型的,是为了保存源目标对象的各个属性的名和值  
  2. public class Memento {  
  3.     private HashMap<String,Object> stateMap;  
  4.   
  5.     public Memento(HashMap<String, Object> stateMap) {  
  6.         this.stateMap = stateMap;  
  7.     }  
  8.       
  9.     public HashMap<String, Object> getStateMap() {  
  10.         return stateMap;  
  11.     }  
  12.   
  13.     public void setStateMap(HashMap<String, Object> stateMap) {  
  14.         this.stateMap = stateMap;  
  15.     }  
  16. }  
[java]  view plain copy
  1. /** 
  2.  * 多备份备份,即可以备份一个对象的多种状态, 
  3.  * 注意这种多备份,不要用于备份特别频繁的地方,容易出现内存溢出, 
  4.  * 或者增加Map的上限,防止出现内存的泄漏。 
  5.  */  
  6. public class Caretaker {  
  7.     private HashMap<String,Memento> mementos=new HashMap<String,Memento>();  
  8.   
  9.     public Memento getMemento(String key) {  
  10.         return mementos.get(key);  
  11.     }  
  12.   
  13.     public void setMemento(String key, Memento memento) {  
  14.         this.mementos.put(key, memento);  
  15.     }  
  16. }  
[java]  view plain copy
  1. /* 
  2.  * 使用此类是为了操作方便 
  3.  */  
  4. public class BeanUtils {  
  5.       
  6.     /* 
  7.      * 把bean的所有属性及数值放到HashMap中 
  8.      */  
  9.     public static HashMap<String,Object> beanToHash(Object bean){  
  10.         HashMap<String,Object> result=new HashMap<String,Object>();  
  11.         try{  
  12.             BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass());//获得bean描述  
  13.             PropertyDescriptor[] descriptors=beanInfo.getPropertyDescriptors();//获得属性描述  
  14.               
  15.             //遍历所有属性  
  16.             for(PropertyDescriptor des : descriptors){  
  17.                 String fieldName=des.getName();//属性名  
  18.                 Method getter=des.getReadMethod();//读取属性的方法  
  19.                 Object fieldValue=getter.invoke(bean, new Object[]{});//读取属性值  
  20.                 if(!fieldName.equalsIgnoreCase("class")){  
  21.                     result.put(fieldName, fieldValue);  
  22.                 }  
  23.             }  
  24.         }catch(Exception e){  
  25.             e.printStackTrace();  
  26.         }  
  27.         return result;  
  28.     }  
  29.       
  30.       
  31.     //把HashMap的值放到bean中  
  32.     public static void hashToBean(Object bean, HashMap<String,Object> result){  
  33.         try{  
  34.             BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass());//获得bean描述  
  35.             PropertyDescriptor[] descriptors=beanInfo.getPropertyDescriptors();//获得属性描述  
  36.               
  37.             //遍历所有属性  
  38.             for(PropertyDescriptor des : descriptors){  
  39.                 String fieldName=des.getName();//属性名  
  40.                 if(result.containsKey(fieldName)){  
  41.                     Method setter=des.getWriteMethod();//写属性的方法  
  42.                     setter.invoke(bean, new Object[]{result.get(fieldName)});//将值写入到属性中  
  43.                 }  
  44.             }  
  45.         }catch(Exception e){  
  46.               
  47.         }  
  48.     }  
  49. }  
[java]  view plain copy
  1. public class Client {  
  2.     public static void main(String[] args) {  
  3.         Originator originator=new Originator();  
  4.         Caretaker caretaker=new Caretaker();  
  5.           
  6.         originator.setState1("brave");  
  7.         originator.setState2("responsibility");  
  8.         originator.setState3("clever");  
  9.         System.out.println("001: "+originator.toString());  
  10.         caretaker.setMemento("001",originator.createMemento());//创建第一个备份  
  11.           
  12.         originator.setState1("weak");  
  13.         originator.setState2("lazy");  
  14.         originator.setState3("temper");  
  15.         System.out.println("002: "+originator.toString());  
  16.         caretaker.setMemento("002",originator.createMemento());//创建第二个备份  
  17.           
  18.         originator.setState1("self-reflection");  
  19.         originator.setState2("get up early");  
  20.         originator.setState3("go to libyary everyday");  
  21.         System.out.println("003: "+originator.toString());  
  22.         caretaker.setMemento("003",originator.createMemento());//创建第三个备份  
  23.           
  24.         originator.restoreMemento(caretaker.getMemento("001"));//恢复  
  25.         System.out.println("now: "+originator.toString());  
  26.           
  27.     }  
  28. }  
多个属性,多个备份嘛,那就把备忘录类和备忘录管理类换成集合类型就可以了。但是这种做法要特别注意内存溢出的问题,不要在备份频繁的地方使用这种备忘录模式,可以设置Map的上限,来防止内存泄露。


另外,备份还可以有另外一种实现方式,备份嘛,自然会联想到复制,也就联想到原型模型了,可以使用克隆的方法来实现备忘录,如下源码:

[java]  view plain copy
  1. /* 
  2.  * 通过克隆对象的方式实现备忘录 
  3.  */  
  4. public class Originator implements Cloneable{  
  5.     private String state;  
  6.   
  7.     public String getState() {  
  8.         return state;  
  9.     }  
  10.   
  11.     public void setState(String state) {  
  12.         this.state = state;  
  13.     }  
  14.       
  15.     public void changeState(){  
  16.         this.state="heart hurt...";  
  17.     }  
  18.   
  19.     public Originator createMemento(){  
  20.         return this.clone();  
  21.     }  
  22.       
  23.     public void restoreMemento(Originator originator){  
  24.         this.setState(originator.getState());  
  25.     }  
  26.       
  27.     @Override  
  28.     protected Originator clone(){  
  29.         try{  
  30.             return (Originator)super.clone();  
  31.         }catch(CloneNotSupportedException e){  
  32.             e.printStackTrace();  
  33.         }  
  34.         return null;  
  35.     }  
  36. }  
[java]  view plain copy
  1. public class Caretaker {  
  2.     private Originator originator;  
  3.   
  4.     public Originator getMemento() {  
  5.         return originator;  
  6.     }  
  7.   
  8.     public void setMemento(Originator originator) {  
  9.         this.originator = originator;  
  10.     }  
  11. }  


还有,如果需要想要增加安全性,即备份的东西是不可以随便改变的,除了原发起对象之外,其他的对象是都不可访问这个备忘录的,我个人现在浅薄的观点认为,只要在备忘录类中不实现那个setMemento()方法,不就可以避免这个类的对象被修改了吗?反正这个方法没有被用到,不知道这样做对不对,《设计模式之禅》的作者有他的做法,即把备忘录类作为原发起对象的一个内部类,这样就可以实现只有发起对象可以访问这个备忘录类了,再让这个内部类实现一个空的接口,让其可以在外和其他类建立关联关系,其实现代码如下:

[java]  view plain copy
  1. public interface IMemento {  
  2.   
  3. }  
[java]  view plain copy
  1. /* 
  2.  * 使用内部类,增强安全性 
  3.  */  
  4. public class Originator {  
  5.     private String state;  
  6.   
  7.     public String getState() {  
  8.         return state;  
  9.     }  
  10.   
  11.     public void setState(String state) {  
  12.         this.state = state;  
  13.     }  
  14.       
  15.     public void changeState(){  
  16.         this.state="heart hurt...";  
  17.     }  
  18.       
  19.     public Memento createMemento(){  
  20.         return new Memento(this.state);  
  21.     }  
  22.       
  23.     public void restoreMemento(IMemento memento){  
  24.         this.setState(((Memento)memento).getState());  
  25.     }  
  26.       
  27.     //把备忘录类写成内部类,这样只有发起人才能访问备忘录,其他对象都不可以访问,这就增加了备忘录的安全性  
  28.     //实现了一个空的接口,以便于在外边和其他类产生关联关系。  
  29.     private class Memento implements IMemento {  
  30.         private String state;  
  31.   
  32.         private Memento(String state) {  
  33.             this.state = state;  
  34.         }  
  35.   
  36.         private String getState() {  
  37.             return state;  
  38.         }  
  39.     }  
  40. }  
[java]  view plain copy
  1. //在原发起类的外部使用备忘录类的空接口建立和其他对象的关联  
  2. public class Caretaker {  
  3.     private IMemento memento;  
  4.   
  5.     public IMemento getMemento() {  
  6.         return memento;  
  7.     }  
  8.   
  9.     public void setMemento(IMemento memento) {  
  10.         this.memento = memento;  
  11.     }  
  12. }  

备忘录模式的使用场景:

1、需要保存和恢复数据的相关状态场景

2、数据库连接的事务管理就是用的备忘录模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值