JAVA设计模式学习22——备忘录模式

2019-03-07 14:03|来源: 网络

备忘录(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)类,它不在需要管理负责备份一个副本在自己内部,当发起人内部状态失效时,可以用外部状态来还原。备忘录模式的缺点是完整保存发起人状态在整个过程中,备忘录角色的资源消耗可能很大,还有就是管理者不知道资源到底多大,外部能不能承担,外部无法预料,也无法给用户一个提醒。    

 
转自:http://alaric.iteye.com/blog/1931253

相关问答

更多
  • 其实我相信你做了3年的java一定了解你所说的设计模式和反射机制在程序早已用过了的把,只是自己平时没注意;设计模式主要是用项目管理,让人看起来通俗易懂.反射机制用到的地方呢也比较多,在用properties和JFreeChart等地方都用到.
  • 你是准备学习使用 还是说研究它的底层实现机制这些东西? 如果只是使用 可以暂时不了解这些 ,你也没有这么多精力来了解这么多 你先将框架中的东西用熟练 然后再去了解这些也不迟 spring 的 IOC aop hibernate的 延迟加载 缓存 ,mvc这些学习都需要学习 其实你学习不学习这几个框架都可以对设计模式进行学习 设计模式在日常的版本开发中好处很多 而且在面试的时候 考官也会问设计模式方面的知识
  • 那么,这是if (i mod 16) = 0测试,截断16个字符的行。 我相信记事本和这段代码一样: var i: Integer; s: AnsiString; Stream: TFileStream; begin Stream := TFileStream.Create(FileName, fmOpenRead); try SetLength(s, Stream.Size); if Stream.Size>0 then Stream.ReadBuffer( ...
  • 我认为你应该重新思考。 我意识到您的控件只能在只读模式下使用,但是,如果最终用户希望复制部分文本,该怎么办? 然后他需要能够选择有问题的部分。 不过,如果您确定需要禁用每种选择,最简单的方法是使用TRichEdit而不是TMemo ,并且只需执行 procedure TForm1.RichEdit1SelectionChange(Sender: TObject); begin RichEdit1.SelLength := 0; end; I think you should rethink. I re ...
  • 我决定使用TPopupmenu作为自动完成列表为TMemo编写一些处理程序。 对于那些阅读此内容的人,请参阅我的另一篇文章: Delphi - 在备忘录中获取插入符号的全部内容 (感谢RRUZ) 以下代码:OnPopup for AutoComplete TPopupMenu :( memoAutoComplete保存自动完成项列表) procedure AutoCompletePopup(Sender: TObject); var i : integer; NewItem : TMenuItem; Aut ...
  • 您可能已经注意到了WORKORDER对象上的WOSTATUS关系,并发现您无法控制为该工作订单选择了多个WOSTATUS记录中的哪一个来显示备忘录。 你需要制作一份这种关系的副本,专门找到最新的记录。 要找到最新的记录,您可以使用CHANGEDATE匹配工作订单上的STATUSDATE或WOSTATUSID最高的WOSTATUS记录。 假设你选择前者,因为它不需要子查询,所以你将创建一个从WORKORDER到WOSTATUS的新关系,名为LASTSTATUS,其中有一个where子句,如下所示: wonum ...
  • 您可以使用弹出菜单的PopupComponent属性: procedure TForm1.PopupItemClick(Sender: TObject); begin if PopupMenu1.PopupComponent = Memo1 then .. else .. You can use the PopupComponent property of the popup menu: procedure TForm1.PopupItemClick(Sender: TObject ...
  • Memoization在这里工作得很好。 事实上,它运作良好,它填补了你所有的记忆。 Collatz序列的中间术语变得非常大。 从1到1000000开始的任何序列中出现的最大术语是数字2974984576 。 所以这是您尝试在内存中构建的列表的长度。 另一方面,只是直接实现没有记忆的Collatz功能应该可以解决这个问题。 Memoization is working just fine here. In fact, it's working so well that it fills up all you ...
  • 到目前为止,不允许访问本机ios录音机录制的语音备忘录。 最好的选择是使用AVAudioRecorder,让用户在你的应用程序中记录他们自己的备忘录,上传到服务器并访问这些备忘录以显示录制的备忘录。 Till date it is not allowed to access voice memos recorded by native ios recorder. The best option is to use AVAudioRecorder and let the user record their o ...
  • 这里有一些示例代码可以让您尝试排序。 它在每一行上使用文本值和数字,用制表符分隔( #9 )。 每个按钮单击处理程序的开头都有代码,它将文本重置为相同的起始值,因此您可以看到效果。 第一个按钮( btnNameSort )使用标准TStringList.Sort按文本值排序,第二个按钮( btnNameSort )使用TListSortCompare自定义排序函数按数值排序。 // Simply uses TStringList.Sort to sort in the default (alpha) ord ...