备忘录模式

备忘录(Memento)模式:又叫做快照模式(Snapshot Pattern)或Token模式,属于行为模式。在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
备忘录模式有如下结构图:
备忘录模式涉及角色如下:
发起人(Originator):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。
备忘录 ( Memento ):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
管理者(Caretaker):负责备忘录Memento,不能对Memento的内容进行访问或者操作。
对上面结构图的代码模拟:
Java代码   收藏代码
  1. package memento;  
  2. /** 
  3.  *  
  4.  *作者:alaric 
  5.  *时间:2013-8-25下午2:04:27 
  6.  *描述:备忘录角色 
  7.  */  
  8. public class Memento {  
  9.   
  10.     private String state;  
  11.       
  12.     public Memento(String state){  
  13.         this.state = state;  
  14.     }  
  15.   
  16.     public String getState() {  
  17.         return state;  
  18.     }  
  19.   
  20.     public void setState(String state) {  
  21.         this.state = state;  
  22.     }     
  23.       
  24. }  
 
Java代码   收藏代码
  1. package memento;  
  2. /** 
  3.  *  
  4.  *作者:alaric 
  5.  *时间:2013-8-25下午2:48:32 
  6.  *描述:发起人 
  7.  */  
  8. public class Originator {  
  9.   
  10.     private String state;  
  11.       
  12.     public Memento createMemento(){  
  13.         return new Memento(state);  
  14.     }  
  15.       
  16.     /** 
  17.      *  
  18.      *作者:alaric 
  19.      *时间:2013-8-25下午4:05:39 
  20.      *描述:还原 
  21.      */  
  22.     public void restoreMemento(Memento memento){  
  23.         this.state = memento.getState();  
  24.     }  
  25.   
  26.     public String getState() {  
  27.         return state;  
  28.     }  
  29.   
  30.     public void setState(String state) {  
  31.         this.state = state;  
  32.     }  
  33. }  
 
Java代码   收藏代码
  1. package memento;  
  2. /** 
  3.  *  
  4.  *作者:alaric 
  5.  *时间:2013-8-25下午2:48:05 
  6.  *描述:管理者 
  7.  */  
  8. public class Caretaker {  
  9.   
  10.     private Memento memento;  
  11.       
  12.     /** 
  13.      *  
  14.      *作者:alaric 
  15.      *时间:2013-8-25下午3:47:18 
  16.      *描述:取值 
  17.      */  
  18.     public Memento retrieveMemento(){  
  19.         return memento;  
  20.     }  
  21.     /** 
  22.      *  
  23.      *作者:alaric 
  24.      *时间:2013-8-25下午4:05:01 
  25.      *描述:设置 
  26.      */  
  27.     public void saveMemento(Memento memento){  
  28.         this.memento = memento;  
  29.     }  
  30. }  
 
Java代码   收藏代码
  1. package memento;  
  2. /** 
  3.  *  
  4.  *作者:alaric 
  5.  *时间:2013-8-25下午2:03:49 
  6.  *描述:测试类 
  7.  */  
  8. public class Client {  
  9.   
  10.     private static Originator o = new Originator();  
  11.     private static Caretaker c = new Caretaker();  
  12.       
  13.     /** 
  14.      *作者:alaric 
  15.      *时间:2013-8-25下午2:03:43 
  16.      *描述: 
  17.      */  
  18.     public static void main(String[] args) {  
  19.         //改变发起人的状态  
  20.         o.setState("on");  
  21.         //创建备忘录对象,并保持于管理保持  
  22.         c.saveMemento(o.createMemento());  
  23.         //改变发起人的状态  
  24.         o.setState("off");  
  25.         //还原状态  
  26.         o.restoreMemento(c.retrieveMemento());  
  27.     }  
  28.   
  29. }  
 对于上述描述中,客户端语句o.createMemento()得到备忘录后,是可以直接获取备忘录中信息的,因为备忘录类没有提供窄接口,这样就破坏了原有的封装性。这种设计备忘录角色的内部所存状态对所有对象是公开的,所以叫做"白箱"实现。有白箱就有黑箱,“黑箱”的实现方式就是利用java双接口的方式来隔离不同的对象访问的。
 
什么是双接口?就是一个类实现两个接口,不同的类看到的是不同的类型,就像蝙蝠一样,在老鼠一块他就展现的是老鼠的接口;在鸟一块就展现的是鸟的接口。

 对于备忘录来说实现双接口,给发起人(Originator)角色展现宽接口,给管理者管理者(Caretaker)提供窄接口。宽接口由Memento本身就可以展现,窄接口只是个标识接口,不提供任何操作的方法。接下来看看如何用双接口方式“ 黑箱”实现。
首先以Memento以标识接口方式提供给除了发起人角色以外的对象,然后把Memento类作为Originaator的内部类,并用private来修饰,保证外部无法操作此类。结构图如下:

 描述代码如下:
Java代码   收藏代码
  1. package memento.black;  
  2. /** 
  3.  *  
  4.  *作者:alaric 
  5.  *时间:2013-8-25下午6:12:48 
  6.  *描述:标识接口 
  7.  */  
  8. public interface IMemento {  
  9.   
  10. }  
 
Java代码   收藏代码
  1. package memento.black;  
  2. /** 
  3.  *  
  4.  *作者:alaric 
  5.  *时间:2013-8-25下午2:48:32 
  6.  *描述:发起人 
  7.  */  
  8. public class Originator {  
  9.   
  10.     private String state;  
  11.       
  12.     /** 
  13.      *  
  14.      *作者:alaric 
  15.      *时间:2013-8-25下午6:18:36 
  16.      *描述:穿件备忘录 
  17.      */  
  18.     public IMemento createMemento(){  
  19.         return (IMemento) new Memento(state);  
  20.     }  
  21.       
  22.     /** 
  23.      *  
  24.      *作者:alaric 
  25.      *时间:2013-8-25下午4:05:39 
  26.      *描述:还原 
  27.      */  
  28.     public void restoreMemento(IMemento memento){  
  29.         Memento m = (Memento) memento;  
  30.         setState(m.getState());  
  31.     }  
  32.   
  33.     public String getState() {  
  34.         return state;  
  35.     }  
  36.   
  37.     public void setState(String state) {  
  38.         this.state = state;  
  39.         System.out.println("current state:"+state);  
  40.     }  
  41.     /** 
  42.      *  
  43.      *作者:alaric 
  44.      *时间:2013-8-25下午9:22:02 
  45.      *描述:内部类 
  46.      */  
  47.     public class Memento implements IMemento{  
  48.   
  49.         private String state;  
  50.           
  51.         public Memento(String state){  
  52.             this.state = state;  
  53.         }  
  54.   
  55.         public String getState() {  
  56.             return state;  
  57.         }  
  58.   
  59.         public void setState(String state) {  
  60.             this.state = state;  
  61.         }     
  62.     }  
  63. }  
 
Java代码   收藏代码
  1. package memento.black;  
  2. /** 
  3.  *  
  4.  *作者:alaric 
  5.  *时间:2013-8-25下午2:48:05 
  6.  *描述:管理者 
  7.  */  
  8. public class Caretaker {  
  9.   
  10.     private IMemento memento;  
  11.       
  12.     /** 
  13.      *  
  14.      *作者:alaric 
  15.      *时间:2013-8-25下午3:47:18 
  16.      *描述:取值 
  17.      */  
  18.     public IMemento retrieveMemento(){  
  19.         return memento;  
  20.     }  
  21.     /** 
  22.      *  
  23.      *作者:alaric 
  24.      *时间:2013-8-25下午4:05:01 
  25.      *描述:设值 
  26.      */  
  27.     public void saveMemento(IMemento memento){  
  28.         this.memento = memento;  
  29.     }  
  30. }  
 
Java代码   收藏代码
  1. package memento.black;  
  2.   
  3.   
  4. /** 
  5.  *  
  6.  *作者:alaric 
  7.  *时间:2013-8-25下午2:03:49 
  8.  *描述:测试类 
  9.  */  
  10. public class Client {  
  11.   
  12.     private static Originator o = new Originator();  
  13.     private static Caretaker c = new Caretaker();  
  14.       
  15.     /** 
  16.      *作者:alaric 
  17.      *时间:2013-8-25下午2:03:43 
  18.      *描述: 
  19.      */  
  20.     public static void main(String[] args) {  
  21.         //改变发起人的状态  
  22.         o.setState("on");  
  23.         //创建备忘录对象,并保持于管理保持  
  24.         c.saveMemento(o.createMemento());  
  25.           
  26.         //改变发起人的状态  
  27.         o.setState("off");  
  28.         //还原状态  
  29.         o.restoreMemento(c.retrieveMemento());  
  30.     }  
  31.   
  32. }  
 运行结果:
current state:on
current state:off
current state:on
 
举个栗子,数据库系统设定一天一个全备,10分钟一个差备,当数据库系统出现问题的时候,就可以还原最近的一个备份。数据库的备份是一个黑箱的,在没还原之前,一个备份文件我们看不出里面都是什么样的数据,所以这里用黑箱实现来描述,先给出类结构图如下:

 描述代码如下:
Java代码   收藏代码
  1. package memento.example;  
  2. /** 
  3.  *  
  4.  *作者:alaric 
  5.  *时间:2013-8-25下午6:12:48 
  6.  *描述:标识接口 
  7.  */  
  8. public interface IMemento {  
  9.   
  10. }  
 
Java代码   收藏代码
  1. package memento.example;  
  2.   
  3.   
  4. /** 
  5.  *  
  6.  *作者:alaric 
  7.  *时间:2013-8-25下午2:48:32 
  8.  *描述:数据库系统(发起人角色) 
  9.  */  
  10. public class DatabaseServer {  
  11.   
  12.     private boolean isUseable;  
  13.       
  14.     /** 
  15.      *  
  16.      *作者:alaric 
  17.      *时间:2013-8-25下午6:18:36 
  18.      *描述:穿件备忘录 
  19.      */  
  20.     public IMemento createMemento(){  
  21.         return (IMemento) new Memento(isUseable);  
  22.     }  
  23.       
  24.     /** 
  25.      *  
  26.      *作者:alaric 
  27.      *时间:2013-8-25下午4:05:39 
  28.      *描述:还原 
  29.      */  
  30.     public boolean restoreMemento(IMemento memento){  
  31.         Memento m = (Memento) memento;  
  32.         setUseable(m.isUseable());  
  33.         return this.isUseable;  
  34.     }  
  35.   
  36.       
  37.     public boolean isUseable() {  
  38.         return isUseable;  
  39.     }  
  40.   
  41.     public void setUseable(boolean isUseable) {  
  42.         this.isUseable = isUseable;  
  43.         System.out.println("DB state useable is: "+isUseable);  
  44.     }  
  45.   
  46.   
  47.     /** 
  48.      *  
  49.      *作者:alaric 
  50.      *时间:2013-8-25下午9:22:02 
  51.      *描述:内部类 
  52.      */  
  53.     public class Memento implements IMemento{  
  54.   
  55.         private boolean isUseable;  
  56.           
  57.         public Memento(boolean isUseable) {  
  58.             super();  
  59.             this.isUseable = isUseable;  
  60.         }  
  61.   
  62.         public boolean isUseable() {  
  63.             return isUseable;  
  64.         }  
  65.   
  66.         public void setUseable(boolean isUseable) {  
  67.             this.isUseable = isUseable;  
  68.         }  
  69.   
  70.           
  71.     }  
  72. }  
 
Java代码   收藏代码
  1. package memento.example;  
  2.   
  3. import java.util.Date;  
  4. import java.util.Iterator;  
  5. import java.util.Map;  
  6. import java.util.concurrent.ConcurrentHashMap;  
  7.   
  8. /** 
  9.  *  
  10.  *作者:alaric 
  11.  *时间:2013-8-25下午2:48:05 
  12.  *描述:备份服务器(管理者) 
  13.  */  
  14. public class BackupsServer {  
  15.     private DatabaseServer dbServer ;//增强管理者的功能,把发起人的操作放在这里  
  16.     private Map<Long,IMemento> mementos ;//用一个map来对数据库服务多点备份  
  17.       
  18.     public BackupsServer(DatabaseServer dbServer) {  
  19.         super();  
  20.         this.dbServer = dbServer;  
  21.         mementos = new ConcurrentHashMap<>();  
  22.     }  
  23.       
  24.     /** 
  25.      *  
  26.      *作者:alaric 
  27.      *时间:2013-8-25下午3:47:18 
  28.      *描述:还原 
  29.      */  
  30.     public void retrieveMemento(){  
  31.         Iterator<Long> it = mementos.keySet().iterator();  
  32.         //还原到最近一个可用的状态  
  33.         while(it.hasNext()){  
  34.             Long key = it.next();  
  35.             IMemento val = mementos.get(key);  
  36.             boolean isUseable = dbServer.restoreMemento(val);  
  37.             if(isUseable){  
  38.                 break;  
  39.             }  
  40.         }  
  41.     }  
  42.     /** 
  43.      *  
  44.      *作者:alaric 
  45.      *时间:2013-8-25下午4:05:01 
  46.      *描述:备份 
  47.      */  
  48.     public void createMemento(){  
  49.         IMemento memento = dbServer.createMemento();  
  50.         this.mementos.put(new Date().getTime(), memento);  
  51.     }  
  52.   
  53.   
  54. }  
 
Java代码   收藏代码
  1. package memento.example;  
  2.   
  3. import java.util.Map;  
  4.   
  5. import memento.Caretaker;  
  6.   
  7.   
  8. /** 
  9.  *  
  10.  *作者:alaric 
  11.  *时间:2013-8-25下午2:03:49 
  12.  *描述:测试类 
  13.  */  
  14. public class Client {  
  15.   
  16.       
  17.     private static DatabaseServer dbServer = new DatabaseServer();  
  18.     private static BackupsServer backupServer = new BackupsServer(dbServer);  
  19.       
  20.     /** 
  21.      *作者:alaric 
  22.      *时间:2013-8-25下午2:03:43 
  23.      *描述: 
  24.      * @throws InterruptedException  
  25.      */  
  26.     public static void main(String[] args) throws InterruptedException {  
  27.         //数据库系统设置可用状态  
  28.         dbServer.setUseable(true);  
  29.         //备份  
  30.         backupServer.createMemento();  
  31.           
  32.         //1秒钟备份一次  
  33.         Thread.sleep(1000);  
  34.         dbServer.setUseable(true);  
  35.         backupServer.createMemento();  
  36.           
  37.         Thread.sleep(1000);  
  38.         dbServer.setUseable(true);  
  39.         backupServer.createMemento();  
  40.           
  41.         Thread.sleep(1000);  
  42.         //设置系统故障  
  43.         dbServer.setUseable(false);  
  44.         //系统故障立即还原到最近一次可用状态  
  45.         System.out.println("------系统还原-----");  
  46.         backupServer.retrieveMemento();  
  47.     }  
  48.   
  49. }  
 运行结果:
DB state useable is: true
DB state useable is: true
DB state useable is: true
DB state useable is: false
------系统还原-----
DB state useable is: true
 
上述代码利用黑箱实现方式模拟了数据库备份还原过程。备忘录模式优点是简化了发起人(Originator)类,它不在需要管理负责备份一个副本在自己内部,当发起人内部状态失效时,可以用外部状态来还原。备忘录模式的缺点是完整保存发起人状态在整个过程中,备忘录角色的资源消耗可能很大,还有就是管理者不知道资源到底多大,外部能不能承担,外部无法预料,也无法给用户一个提醒。

猜你喜欢

转载自zhengcb9872.iteye.com/blog/1931355