设计模式之备忘录模式

  备忘录模式,翻译成标记模式好一些,因为这种设计模式的目的是为了反悔:GOF给备忘录模式的定义为:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

  在之前的命令模式中,我们曾经提到利用中间的命令角色可以实现undo、redo 的功能。从定义可以看出备忘录模式是专门来存放对象历史状态的,这对于很好的实现undo、redo功能有很大的帮助。所以在命令模式中undo、redo 功能可以配合备忘录模式来实现。

  如果单就实现保存一个对象在某一时刻的状态的功能,是很简单的,但是为了能让备份对象访问到原对象中属性,意味着可能暴露了对象的私有属性,会破坏封装。那么备忘录模式是如何保持封装性呢?

  来看看备忘录模式的结构:

  1) 备忘录(Memento)角色:备忘录角色存储“备忘发起角色”的内部状态。“备忘发起角色”根据需要决定备忘录角色存储“备忘发起角色”的哪些内部状态。为了防止“备忘发起角色”以外的其他对象访问备忘录。备忘录实际上有两个接口,“备忘录管理者角色”只能看到备忘录提供的窄接口——对于备忘录角色中存放的属性是不可见的。“备忘发起角色”则能够看到一个宽接口——能够得到自己放入备忘录角色中属性。
  2) 备忘发起(Originator)角色:“备忘发起角色”创建一个备忘录,用以记录当前时刻它的内部状态。在需要时使用备忘录恢复内部状态。
  3) 备忘录管理者(Caretaker)角色:负责保存好备忘录。不能对备忘录的内容进行操作或检查。

  按照定义中的要求,备忘录角色要保持完整的封装。最好的情况便是:备忘录角色只应该暴露操作内部存储属性的的接口给“备忘发起角色”。而对于其他角色则是不可见的。

  下面对三种在 Java 中可保存封装的方法进行探讨。
  第一种就是采用两个不同的接口类来限制访问权限。这两个接口类中,一个提供比较完备的操作状态的方法,我们称它为宽接口;而另一个则可以只是一个标示,我们称它为窄接口。备忘录角色要实现这两个接口类。这样对于“备忘发起角色”采用宽接口进行访问,而对于其他的角色或者对象则采用窄接口进行访问。这种实现比较简单,但是需要人为的进行规范约束——而这往往是没有力度的。
  第二种方法便很好的解决了第一种的缺陷:采用内部类来控制访问权限。将备忘录角色作为“备忘发起角色”的一个私有内部类。好处我不详细解释了,看看代码吧就明白了。下面的代码是一个完整的备忘录模式的教学程序。它便采用了第二种方法来实现备忘录模式。

 1 class Originator{
 2     //这个是要保存的状态
 3    private int state= 90;
 4     //保持一个“备忘录管理者角色”的对象
 5    private Caretaker c = new Caretaker();
 6     //读取备忘录角色以恢复以前的状态
 7    public void setMemento(){
 8         Memento memento = (Memento)c.getMemento();
 9         state = memento.getState();
10         System.out.println("the state is "+state+" now");
11     }
12     //创建一个备忘录角色,并将当前状态属性存入,托给“备忘录管理者角色”存放。
13    public void createMemento(){
14         c.saveMemento(new Memento(state));
15     }
16     //this is other business methods...
17     //they maybe modify the attribute state
18     public void modifyState4Test(int m){
19         state = m;
20         System.out.println("the state is "+state+" now");
21     }
22     //作为私有内部类的备忘录角色,它实现了窄接口,可以看到在第二种方法中宽接口已经不再需要
23    //注意:里面的属性和方法都是私有的
24    private class Memento implements MementoIF{
25         private int state ;
26         private Memento(int state){
27            this.state = state ;
28         }
29         private int getState(){
30            return state;
31          }
32    }
33 }
34 //测试代码——客户程序
35 public class TestInnerClass{
36     public static void main(String[] args){
37         Originator o = new Originator();
38         o.createMemento();
39         o.modifyState4Test(80);
40         o.setMemento();
41     }
42 }
43 //窄接口
44 interface MementoIF{}
45     //“备忘录管理者角色”
46     class Caretaker{
47         private MementoIF m ;
48         public void saveMemento(MementoIF m){
49         this.m = m;
50     }
51     public MementoIF getMemento(){
52         return m;
53     }
54 }

  第三种方式是不太推荐使用的:使用 clone 方法来简化备忘录模式。由于Java 提供了clone 机制,这使得复制一个对象变得轻松起来。使用了clone 机制的备忘录模式,备忘录角色基本可以省略了,而且可以很好的保持对象的封装。但是在为你的类实现clone 方法时一定要慎重。

posted @ 2014-12-09 16:43  Simba.Chen  阅读(426)  评论(0编辑  收藏  举报