七、备忘录模式Memento(行为型模式)

其目的是,在不违反封装原则的前提下.採集和备份一个对象的内部状态以便这个对象能够在以后恢复到之前的某个状态.


在Memento模式中,有例如以下角色:
1.Memento (备忘录)
* 存储Originator的内部状态. 依据Originator的须要.能够存储随意的内部状态.
* 保护对状态的訪问.除了Originator对象外,其它对象不能訪问其存储的内部状态.Memeoto实际上提供了2个接口.
Caretaker仅仅能訪问Memento的窄接口(narrow interface) - 它仅仅能够把memento传递给其它对象.
相反,Originator能够訪问Memento的宽接口(wide interface). 通过这个接口,能够让Originator获取足够的信息以恢复到之前的状态.理想情况下,仅仅有创建memento的Originator才有权限訪问memento的内部状态信息.
2.Originator (发起人)
* 能够创建一个新的memento, 并把自己的当前状态信息存储到memento里面
* 能够使用memento来恢复其内部状态信息
3.Caretaker (负责人)
* 负责维护和管理memento对象
* 从不正确memento的内容进行操作或者測试



备忘录模式的操作过程 
1、client为发起人角色创建一个备忘录对象。

 
2、调用发起人对象的某个操作。这个操作是能够撤销的。

 
3、检查发起人对象所出状态的有效性。检查的方式能够是发起人对象的内部自查。也能够由某个外部对象进行检查。 
4、假设须要的话。将发起人的操作撤销。也就是说依据备忘录对象的记录。将发起人对象的状态恢复过来。 

“假如”协议模式的操作过程: 
1、将发起人对象做一个拷贝。

 
2、在拷贝上运行某个操作。

 
3、检查这个拷贝的状态是否有效和自恰。 
4、假设检查结果是无效或者不自恰的,那么扔掉这个拷贝,并触发异常处理程序。相反。假设检查是有效和自恰的,那么在原对象上运行这个操作 
显然这一做法对于撤销一个操作并恢复操作前状态较为复杂和困难的发起人对象来说是一个较为慎重和有效的做法。 

“假如”协议模式的长处和缺点 
详细来说,这个做法的好处是能够保证发起人对象永远不会处于无效或不自恰的状态上。这样作的短处是成功的操作必须运行两次。 
假设操作的成功率较低的话。这样做就比較划算。反之就不太划算。

 


使用备忘录模式的长处和缺点 
一、备忘录模式的长处 
1、有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,可是必须要由发起人对象自己读取,这时, 
   使用备忘录模式能够把复杂的发起人内部信息对其它的对象屏蔽起来。从而能够恰当地保持封装的边界。 
2、本模式简化了发起人类。发起人不再须要管理和保存其内部状态的一个个版本号,client能够自行管理他们所需 
   要的这些状态的版本号。

 
3、当发起人角色的状态改变的时候,有可能这个状态无效。这时候就能够使用临时存储起来的备忘录将状态复原。 
二、备忘录模式的缺点: 
1、假设发起人角色的状态须要完整地存储到备忘录对象中。那么在资源消耗上面备忘录对象会非常昂贵。

 
2、当负责人角色将一个备忘录 存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法 
   提醒用户一个操作是否非常昂贵。882——P  
3、当发起人角色的状态改变的时候,有可能这个协议无效。

假设状态改变的成功率不高的话,不如採取“假如”协议模式。


(1)宽接口和白箱:
public class Client {

   private static Originator originator = new Originator();

   private static Caretaker c = new Caretaker();

   public static void main(String[] args) {

      // 该发起人对象的状态

      originator.setState("On");

      // 创建备忘录对象,并将发起人对象的状态存储起来

      c.saveMemento(originator.createMemento());

      // 改动发起人对象的状态

      originator.setState("Off");

      // 恢复发起人对象的状态

      originator.restoreMemento(c.retrieveMemento());

   }

}

// 发起人角色

class Originator {

   private String state;

   // 工厂方法,返还一个新的备忘录对象

   public Memento createMemento() {

      return new Memento(state);

   }

   // 将发起人恢复到备忘录对象所记载的状态

   public void restoreMemento(Memento memento) {

      this.state = memento.getState();

   }

   // 状态的取值方法

   public String getState() {

      return this.state;

   }

   // 状态的赋值方法

   public void setState(String state) {

      this.state = state;

      System.out.println("Current state = " + this.state);

   }

}
/*

 * 备忘录模式要求备忘录对象提供两个不同的接口:一个宽接口提供给发起人对象,还有一个窄接口提供给全部其它的对象,包含负责人对象。

* 宽接口同意发起人读取到全部的数据。窄接口仅仅同意它把备忘录对象传给其它的对象而看不到内部的数据。 */ // 备忘录角色 class Memento { private String state; public Memento(String state) { this.state = state; } public String getState() { return this.state; } public void setState(String state) { this.state = state; } }


/*

 * 负责人角色负责保存备忘录对象,可是从不改动(甚至不查看)备忘录对象的内容(一个更好的实现是负责人对象根本无法从备忘录 对象中读取个改动其内容)

 */

// 负责人角色

class Caretaker {

   private Memento memento;

   // 备忘录的取值方法

   public Memento retrieveMemento() {

      return this.memento;

   }

   // 备忘录的赋值方法

   public void saveMemento(Memento memento) {

      this.memento = memento;

   }

}

首先将发起人对象的状态设置成“On”(或者不论什么有效状态),而且创建一个备忘录对象将这个状态存储起来。然后将发起人对象的状态改成“Off”(或者不论什么状态);最后又将发起人对象恢复到备忘录对象所存储起来的状态,即“On”状态(或者先前所存储的不论什么状态)
备忘录系统运行的时序是这种:
(1)将发起人对象的状态设置成“On”。
(2)调用发起人角色的createMemento()方法,创建一个备忘录对象将这个状态存储起来。
(3)将备忘录对象存储到负责人对象中去。
备忘录系统恢复的时序是这种:
(1)将发起人对象的状态设置成“Off”;
(2)将备忘录对象从负责人对象中取出;
(3)将发起人对象恢复到备忘录对象所存储起来的状态。“On”状态。
白箱实现的优缺点
白箱实现的一个明显的好处是比較简单,因此经常常使用做教学目的。

白箱实现的一个明显的缺点是破坏对发起人状态的封装。

(2)窄接口或者黑箱实现:

//client

public class Client {

   private static Originator originator = new Originator();

   private static Caretaker c = new Caretaker();

   public static void main(String[] args) {

      // 该发起人对象的状态

      originator.setState("On");

      // 创建备忘录对象。并将发起人对象的状态存储起来

      c.saveMemento(originator.createMemento());

      // 改动发起人对象的状态

      originator.setState("Off");

      // 恢复发起人对象的状态

      originator.restoreMemento(c.retrieveMemento());

   }

}
// 发起人角色

class Originator {

   private String state;

   public Originator() {

   }

   // 工厂方法,返还一个新的备忘录对象

   public MementoIF createMemento() {

      return new Memento(this.state);

   }

   // 将发起人恢复到备忘录对象记录的状态

   public void restoreMemento(MementoIF memento) {

      Memento aMemento = (Memento) memento;

      this.setState(aMemento.getState());

   }

   public String getState() {

      return this.state;

   }

   public void setState(String state) {

      this.state = state;

      System.out.println("state =" + state);

   }

   protected class Memento implements MementoIF {

      private String savedState;

      public Memento(String someState) {

        this.savedState = someState;

      }

      private void setState(String someState) {

        savedState = someState;

      }

      private String getState() {

        return savedState;

      }

   }

}

interface MementoIF {

}

// 备忘录角色

class Memento implements MementoIF {

   private String state;

   public Memento(String state) {

      this.state = state;

   }

   public String getState() {

      return this.state;

   }

   public void setState(String state) {

      this.state = state;

   }

}

class Caretaker {

   private MementoIF memento;

   public MementoIF retrieveMemento() {

      return this.memento;

   }

   public void saveMemento(MementoIF memento) {

      this.memento = memento;

   }

}	

黑箱实现运行时的时序为;
  (1)将发起人对象的状态设置成“On”。
(2)调用发起人角色的 createMemento()方法。创建一个备忘录对象将这个状态存储起来。


(3)将备忘录对象存储到负责人对象中去。因为负责人对象拿到的仅是 MementoIF类型,因此无法读出备忘录内部的状态。
恢复时的时序为:
(1)将发起人对象的状态设置成“Off”。
(2)将备忘录对象从负责人对象中取出。

注意此时仅能得到 MementoIF接口,因此无法读出此对象的内部状态
(3)将发起人对象的状态恢复成备忘录对象所存储起来的状态。,因为发起人对象的内部类Memento实现了MementoIF接口
这个内部类是传入的备忘录对象的真实类型,因此发起人对象能够利用内部类Memento 的私有 接口读出此对象的内部状态。

(3)存储多个状态的备忘录模式:

//发起人角色
		import java.util.Vector;
		import java.util.Enumeration;
		
		public class Originator{
			private Vector states;
			private int index;
			
			public Originator(){
				states = new Vector();
				index = 0;
			}
			
			public Memento createMementor(){
				return new Mementor(states,index);
			}
			
			public void restoreMementor(Mementor memento){
				states = memento.getStates();
				index = memento.getIndex()。
			}
			
			public void setState(String state){
				this.states.addElement(state);
				index ++;
			}
			
			//辅助方法。打印出全部的状态
			public void printStates(){
				System.out.println("Total number of states: " + index);
				for(Enumeration e = states.elements();e.hasMoreElements();){
					system.out.println(e.nextElement());
				}
			}
		} 
//备忘录角色
		import java.util.Vector;
		
		public class Memento{
			private Vector states;
			private int index;
			//<span style="font-size: 14px; font-family: Arial, Helvetica, sans-serif;">备忘录的构造子克隆了传入的states,然后将克隆存入到备忘录对象内部,这是一个重要的细节,因为不这种话,将会</span><pre title="备忘录(Memento Pattern)模式 【行为模式第一篇】" style="font-size: 14px;">		     //将会造成client和备忘录对象持有对同一个Vector对象的引用,也能够同一时候改动这个Vector对象,会造成系统崩溃。
public Memento(Vector states,int index){this.states = (Vector)states.clone();this.index = index;}//状态取值方法Vector getStates(){return states;}//检查点取值方法int getIndex(){return this.index;}}

//负责人角色
		import java.util.Vector;
		
		public class Caretaker{
			private Originator o;
			private Vector mementos = new Vector();
			private int current;
			
			public Caretaker(Originator o){
				this.o = o;
				current = 0;
			}
			
			public int createMemento(){
				Memento memento = o.createMemento();
				mementos.addElement(memento);
				return current ++;
			}
			
			//将发起人恢复到某个检查点
			public void restoreMemento(int index){
				Memento memento = (Memento)mementos.elementAt(index);
				o.restoreMemento(memento);
			}
			
			//某个检查点删除
			public void removeMemento(int index){
				mementos.removeElementAt(index);
			}
		}
		

//client
		public class Client{
			private static Originator o = new Originator();
			private static Caretaker c = new Caretaker(o);
			public static void main(String[] args){
				//改变状态
				o.setState("state 0");
				//建立一个检查点
				c.createMemento();
				//改变状态
				o.setState("state 1");
				
				c.createMemento();
				
				o.setState("state 2");
				
				c.createMemento();
				
				o.setState("state 3");
				
				c.createMemento();
				
				o.setState("state 4");
				
				c.createMemento();
				
				o.printStates();
				
				//恢复到第二个检查点
				System.out.println("Restoring to 2");
				
				c.restoreMemento(2);
				
				o.printStates();
				
				System.out.println("Restoring to 0");
				
				c.restoreMemento(0);
				
				o.printStates();
				
				System.out.println("Restoring to 3");
				
				c.restoreMemento(3);
				
				o.printStates();
				
				
			}
		}

(4)自述历史模式(备忘录模式的一个变种)
因为“自述历史”作为一个备忘录模式的特殊实现形式非常easy易懂。它可能是备忘录模式最为流行的实现形式。

 

//窄接口
		public interface MementoIF{}

//发起人角色
		public class Originator{
			public String state;
			
			public Originator(){}
			
			public void changeState(String state){
				this.state = state;
				System.out.println("State has been changed to : " + state);
			}
			
			public Memento createMemento(){
				return new Memento(this);
			}
			
			public void restoreMemento(MementoIF memento){
				Memento m = (Memento)memento;
				changeState(m.state);
			}
			
			class Memento implements MementoIF{
				private String state;
				
				private String getState(){
					return state;
				}
				
				private Memento(Originator o){
					this.state = o.state;
				}
			}
		}

//client
		public class Client{
			private static Originator o;
			private static MementoIF memento;
			
			public static void main(String args[]){
				o = new Originator();
				o.changeState("State 1");
				memento = o.createMemento();
				o.changeState("State 2");
				o.restoreMemento(memento);
			}
		}







posted @ 2018-02-27 17:30  llguanli  阅读(222)  评论(0编辑  收藏  举报