zoukankan      html  css  js  c++  java
  • 23种设计模式(15):备忘录模式

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

    类型:行为类

    类图:

            我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态。比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回。这时我们便可以使用备忘录模式来实现。

    备忘录模式的结构

    • 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
    • 备忘录:负责存储发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
    • 管理角色:对备忘录进行管理,保存和提供备忘录。

    通用代码实现

    [java] view plaincopy
     
    1. class Originator {  
    2.     private String state = "";  
    3.       
    4.     public String getState() {  
    5.         return state;  
    6.     }  
    7.     public void setState(String state) {  
    8.         this.state = state;  
    9.     }  
    10.     public Memento createMemento(){  
    11.         return new Memento(this.state);  
    12.     }  
    13.     public void restoreMemento(Memento memento){  
    14.         this.setState(memento.getState());  
    15.     }  
    16. }  
    17.   
    18. class Memento {  
    19.     private String state = "";  
    20.     public Memento(String state){  
    21.         this.state = state;  
    22.     }  
    23.     public String getState() {  
    24.         return state;  
    25.     }  
    26.     public void setState(String state) {  
    27.         this.state = state;  
    28.     }  
    29. }  
    30. class Caretaker {  
    31.     private Memento memento;  
    32.     public Memento getMemento(){  
    33.         return memento;  
    34.     }  
    35.     public void setMemento(Memento memento){  
    36.         this.memento = memento;  
    37.     }  
    38. }  
    39. public class Client {  
    40.     public static void main(String[] args){  
    41.         Originator originator = new Originator();  
    42.         originator.setState("状态1");  
    43.         System.out.println("初始状态:"+originator.getState());  
    44.         Caretaker caretaker = new Caretaker();  
    45.         caretaker.setMemento(originator.createMemento());  
    46.         originator.setState("状态2");  
    47.         System.out.println("改变后状态:"+originator.getState());  
    48.         originator.restoreMemento(caretaker.getMemento());  
    49.         System.out.println("恢复后状态:"+originator.getState());  
    50.     }  
    51. }  

            代码演示了一个单状态单备份的例子,逻辑非常简单:Originator类中的state变量需要备份,以便在需要的时候恢复;Memento类中,也有一个state变量,用来存储Originator类中state变量的临时状态;而Caretaker类就是用来管理备忘录类的,用来向备忘录对象中写入状态或者取回状态。

     

    多状态多备份备忘录

           通用代码演示的例子中,Originator类只有一个state变量需要备份,而通常情况下,发起人角色通常是一个javaBean,对象中需要备份的变量不止一个,需要备份的状态也不止一个,这就是多状态多备份备忘录。实现备忘录的方法很多,备忘录模式有很多变形和处理方式,像通用代码那样的方式一般不会用到,多数情况下的备忘录模式,是多状态多备份的。其实实现多状态多备份也很简单,最常用的方法是,我们在Memento中增加一个Map容器来存储所有的状态,在Caretaker类中同样使用一个Map容器才存储所有的备份。下面我们给出一个多状态多备份的例子:

    [java] view plaincopy
     
    1. class Originator {  
    2.     private String state1 = "";  
    3.     private String state2 = "";  
    4.     private String state3 = "";  
    5.   
    6.     public String getState1() {  
    7.         return state1;  
    8.     }  
    9.     public void setState1(String state1) {  
    10.         this.state1 = state1;  
    11.     }  
    12.     public String getState2() {  
    13.         return state2;  
    14.     }  
    15.     public void setState2(String state2) {  
    16.         this.state2 = state2;  
    17.     }  
    18.     public String getState3() {  
    19.         return state3;  
    20.     }  
    21.     public void setState3(String state3) {  
    22.         this.state3 = state3;  
    23.     }  
    24.     public Memento createMemento(){  
    25.         return new Memento(BeanUtils.backupProp(this));  
    26.     }  
    27.       
    28.     public void restoreMemento(Memento memento){  
    29.         BeanUtils.restoreProp(this, memento.getStateMap());  
    30.     }  
    31.     public String toString(){  
    32.         return "state1="+state1+"state2="+state2+"state3="+state3;  
    33.     }  
    34. }  
    35. class Memento {  
    36.     private Map<String, Object> stateMap;  
    37.       
    38.     public Memento(Map<String, Object> map){  
    39.         this.stateMap = map;  
    40.     }  
    41.   
    42.     public Map<String, Object> getStateMap() {  
    43.         return stateMap;  
    44.     }  
    45.   
    46.     public void setStateMap(Map<String, Object> stateMap) {  
    47.         this.stateMap = stateMap;  
    48.     }  
    49. }  
    50. class BeanUtils {  
    51.     public static Map<String, Object> backupProp(Object bean){  
    52.         Map<String, Object> result = new HashMap<String, Object>();  
    53.         try{  
    54.             BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());  
    55.             PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();  
    56.             for(PropertyDescriptor des: descriptors){  
    57.                 String fieldName = des.getName();  
    58.                 Method getter = des.getReadMethod();  
    59.                 Object fieldValue = getter.invoke(bean, new Object[]{});  
    60.                 if(!fieldName.equalsIgnoreCase("class")){  
    61.                     result.put(fieldName, fieldValue);  
    62.                 }  
    63.             }  
    64.               
    65.         }catch(Exception e){  
    66.             e.printStackTrace();  
    67.         }  
    68.         return result;  
    69.     }  
    70.       
    71.     public static void restoreProp(Object bean, Map<String, Object> propMap){  
    72.         try {  
    73.             BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());  
    74.             PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();  
    75.             for(PropertyDescriptor des: descriptors){  
    76.                 String fieldName = des.getName();  
    77.                 if(propMap.containsKey(fieldName)){  
    78.                     Method setter = des.getWriteMethod();  
    79.                     setter.invoke(bean, new Object[]{propMap.get(fieldName)});  
    80.                 }  
    81.             }  
    82.         } catch (Exception e) {  
    83.             e.printStackTrace();  
    84.         }  
    85.     }  
    86. }  
    87. class Caretaker {  
    88.     private Map<String, Memento> memMap = new HashMap<String, Memento>();  
    89.     public Memento getMemento(String index){  
    90.         return memMap.get(index);  
    91.     }  
    92.       
    93.     public void setMemento(String index, Memento memento){  
    94.         this.memMap.put(index, memento);  
    95.     }  
    96. }  
    97. class Client {  
    98.     public static void main(String[] args){  
    99.         Originator ori = new Originator();  
    100.         Caretaker caretaker = new Caretaker();  
    101.         ori.setState1("中国");  
    102.         ori.setState2("强盛");  
    103.         ori.setState3("繁荣");  
    104.         System.out.println("===初始化状态=== "+ori);  
    105.           
    106.         caretaker.setMemento("001",ori.createMemento());  
    107.         ori.setState1("软件");  
    108.         ori.setState2("架构");  
    109.         ori.setState3("优秀");  
    110.         System.out.println("===修改后状态=== "+ori);  
    111.           
    112.         ori.restoreMemento(caretaker.getMemento("001"));  
    113.         System.out.println("===恢复后状态=== "+ori);  
    114.     }  
    115. }  

    备忘录模式的优缺点和适用场景

    备忘录模式的优点有:

    • 当发起人角色中的状态改变时,有可能这是个错误的改变,我们使用备忘录模式就可以把这个错误的改变还原。
    • 备份的状态是保存在发起人角色之外的,这样,发起人角色就不需要对各个备份的状态进行管理。

    备忘录模式的缺点:

    • 在实际应用中,备忘录模式都是多状态和多备份的,发起人角色的状态需要存储到备忘录对象中,对资源的消耗是比较严重的。

    如果有需要提供回滚操作的需求,使用备忘录模式非常适合,比如jdbc的事务操作,文本编辑器的Ctrl+Z恢复等。

  • 相关阅读:
    WCF Server Console
    Restart IIS With Powershell
    RestartService (recursively)
    Copy Files
    Stopping and Starting Dependent Services
    多线程同步控制 ManualResetEvent AutoResetEvent MSDN
    DTD 简介
    Using Powershell to Copy Files to Remote Computers
    Starting and Stopping Services (IIS 6.0)
    java中的NAN和INFINITY
  • 原文地址:https://www.cnblogs.com/tomatoxml/p/3781639.html
Copyright © 2011-2022 走看看