行为模式---之--备忘录模式
备忘录模式又叫快照模式(Snapshot Pattern)或Token模式,是对象的行为模式。
备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化(Externalize),存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。它常常与命令模式和迭代子模式一同使用。
常见的软件系统往往不止存储一个状态,而是需要存储多个状态。这些状态常常是一个对象历史发展的不同阶段的快照,存储这些快照的备忘录对象叫做此对象的历史:某一个快照所处的位置叫做检查点(Check Point)
备忘录模式涉及三个角色:
备忘录(Memento)角色:将发起人对象的内部状态存储起来。备忘录可以根据发起人对象的
判断来决定存储多少发起人对象的内部状态。备忘录可以保护其内容不被发起人对象之外的任何对象读取备忘录有两个等效的接口:1.窄接口:负责人对象(和其他除发起人对象之外的任何对象)看到的是备忘录的窄接口,这个窄接口只允许它把备忘录对象传给其他的对象。2.宽接口:与负责人对象看到的窄接口相反的是,发起人对象可以看到一个宽接口,这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人的内部状态
发起人(Originator)角色:它有如下责任:1.创建一个含有当前的内部状态的备忘录对象2.使用备忘录对象存储其内部状态
负责人(Caretaker)角色:它有如下责任:1.负责保存备忘录对象。2.不检查备忘录对象的内容。
备忘录模式与白箱实现:
在java语言中实现备忘录模式时,实现“宽”和“窄”两个接口并不是容易的事。如果暂时忽略两个接口的区别,仅为备忘录角色提供一个宽接口的话,情况就变得简单得多,但是由于备忘录角色对象任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开。因此这个实现又叫做“白箱实现”。“白箱”实现将发起人角色的状态存储在一个大家都看得到的地方,因此是破坏封装性的。但是通过程序员的自律,同样可以在一定程序上实现模式的大部分用意。它又叫做“白箱”备忘录模式
示例代码:
1 public class Client { 2 private static Originator o = new Originator(); 3 private static Caretaker c = new Caretaker(); 4 public static void main(String[] args) { 5 //改变发起人的对象状态 6 o.setState("On"); 7 //创建备忘录对象,并将发起人对象的状态存储起来 8 c.saveMemento(o.createMemento()); 9 //修改发起人对象的状态 10 o.setState("Off"); 11 //恢复发起人的对象状态 12 o.restoreMemento(c.retrieveMemento()); 13 System.out.println(o.getSate()); 14 } 15 } 16 //发起人角色,它用一个新创建的备忘录对象将自己的内部状态存储起来 17 class Originator{ 18 private String state; 19 //工厂方法,返还一个新的备忘录对象 20 public Memento createMemento(){ 21 return new Memento(state); 22 } 23 //将发起人恢复到备忘录对象所记载的状态 24 public void restoreMemento(Memento memento){ 25 this.state = memento.getState(); 26 } 27 //状态的取值方法 28 public String getSate(){ 29 return this.state; 30 } 31 //状态的赋值方法 32 public void setState(String state){ 33 this.state = state; 34 System.out.println("Current state =" + this.state); 35 } 36 } 37 38 //备忘录角色,它将发起人对象传入的状态存储起来,它会根据发起人对象的判断来决定将发起人的内部状态的多少存储起来 39 class Memento{ 40 private String state; 41 //构造子 42 public Memento(String state){ 43 this.state = state; 44 } 45 //状态取值方法 46 public String getState(){ 47 return this.state; 48 } 49 //状态的赋值方法 50 public void setState(String state){ 51 this.state = state; 52 } 53 } 54 //负责人角色,它负责保存备忘录对象,但不修改备忘录对象内容(一个更好实现是负责人对象根本无法从备忘录对象中读取和修改其内容) 55 class Caretaker{ 56 private Memento memento; 57 //备忘录的取值方法 58 public Memento retrieveMemento(){ 59 return this.memento; 60 } 61 //备忘录的赋值方法 62 public void saveMemento(Memento memento){ 63 this.memento = memento; 64 } 65 }
备忘录模式的黑箱实现:
对于Java语言而言,可以将Memento设成Originator类的内部类,从而将Memento对象封装在Originator里面,在外部提供一个标识接口MementoIF给Caretaker以及其他对象。这样,Originator类看到的是Memento的所有接口,而Caretaker以及其他对象看到的仅仅是标识接口MementoIF所暴露出来的接口。
示例代码:
1 public class Client { 2 private static Originator o = new Originator(); 3 private static Caretaker c = new Caretaker(); 4 public static void main(String[] args) { 5 //改变发起人的对象状态 6 o.setState("On"); 7 //创建备忘录对象,并将发起人对象的状态存储起来 8 c.saveMemento(o.createMemento()); 9 //修改发起人对象的状态 10 o.setState("Off"); 11 //恢复发起人的对象状态 12 o.restoreMemento(c.retriveMemento()); 13 System.out.println(o.getState()); 14 } 15 } 16 //发起人角色,在此类中定义了一个内部的Memento类,由于此类的所有方法都是私有的,因此只有它自己和发起人(Originator)类可以调用 17 class Originator{ 18 private String state; 19 //构造 20 public Originator(){} 21 //工厂方法,返还一个新的备忘录对象 22 public MementoIF createMemento(){ 23 return new Memento(this.state); 24 } 25 //将发想人恢复到备忘录对象记录的状态 26 public void restoreMemento(MementoIF memento){ 27 Memento aMemento = (Memento)memento; 28 this.setState(aMemento.getState()); 29 } 30 //状态的取值方法 31 public String getState(){ 32 return this.state; 33 } 34 //状态的赋值方法 35 public void setState(String state){ 36 this.state = state; 37 System.out.println("state ="+ state); 38 } 39 //内部成员类,备忘录 40 protected class Memento implements MementoIF{ 41 private String savedState; 42 //构造 43 private Memento(String someState){ 44 savedState = someState; 45 } 46 //状态赋值方法 47 private void setState(String someState){ 48 savedState = someState; 49 } 50 //状态取值方法 51 private String getState(){ 52 return savedState; 53 } 54 } 55 } 56 57 //MementoIF角色只是一个标识接口,它就是窄接口,由于它是一个标识接口,所以负责人不可能改变这个备忘录对象 的内容 58 interface MementoIF{} 59 60 //负责人角色 61 class Caretaker{ 62 private MementoIF memento; 63 //备忘录的取值方法 64 public MementoIF retriveMemento(){ 65 return this.memento; 66 } 67 //备忘录的赋值方法 68 public void saveMemento(MementoIF memento){ 69 this.memento = memento; 70 } 71 }
备忘录模式的优点:
1.有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取。这时,使用备忘录可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界
2.本模式简化了发起人类。发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理 它们所需要的这些状态的版本。
3.当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
备忘录模式的缺点:
1.如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵
2.当负责人角色将一个备忘录存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否会很昂贵。
3.当发起人角色的状态改变的时候,有可能这个状态无效。