Chan Chen Coding...

          Eleven: Memento Design Pattern

          Refer to:http://blog.sina.com.cn/s/blog_4a2100f8010142n1.html
          1.1.1.概述
          備忘錄是一個非常容易理解的模式,只需要看一下后面的客戶端測試代碼就能明白備忘錄模式的用意。備忘錄模式就是用來保存對象的某一個狀態,這樣在一定時候,可以通過備忘錄來恢復對象的狀態。
          針對上面所說的用意,實現起來就需要一個類來保存這個狀態,這個類就是備忘錄類。鑒于備忘錄很容易理解,就直接看后面的代碼實現吧。
          1.1.2.代碼實現
          在代碼實現中,備忘錄模式一共有三個類,Originator類是原始的對象,正是這個對象的狀態需要備忘,這樣是為了在一個時間,這個對象可以恢復到備忘的狀態。還有一個類是Memento,這個是備忘錄,用于保存Originator類的狀態。還有一個類CareTaker用來管理備忘錄。
          首先看Originator的代碼實現。
              /// <summary>
              /// 用于創建備忘錄的原發器,記錄當前的內部狀態
              /// </summary>
              public class Originator
              {
                  /// <summary>
                  /// 當前的狀態
                  /// </summary>
                  private string state;
           
                  /// <summary>
                  /// 創建一個當前狀態的備忘錄
                  /// </summary>
                  /// <returns></returns>
                  public Memento CreateMemento()
                  {
                      return new Memento(this.state);
                  }
           
                  /// <summary>
                  /// 恢復當前狀態為備忘錄所保存的狀態
                  /// </summary>
                  /// <param name="memento"></param>
                  public void RestoreMemento(Memento memento)
                  {
                      this.state = memento.GetState();
                  }
           
                  public string GetState()
                  {
                      return this.state;
                  }
           
                  public void SetState(string state)
                  {
                      this.state = state;
                  }
           
              }
          Originator類具有一個狀態屬性,這個屬性代表了Originator對象的實時狀態,CreateMemento()方法用來保存Originator對象的一個狀態作為備忘,在以后需要的時候可以顯示。也就是說通過這個方法就可以給Originator對象產生一個備忘錄。RestoreMemento()方法用來從保存的備忘錄中恢復Originator的狀態,將Originator的狀態設置為之前的一個備忘狀態。
          下面是備忘錄類Memento類的代碼實現。
              /// <summary>
              /// 備忘錄類
              /// </summary>
              public class Memento
              {
                  /// <summary>
                  /// 備忘錄所保存的狀態
                  /// </summary>
                  private string state;
           
                  /// <summary>
                  /// 構造函數
                  /// </summary>
                  /// <param name="state"></param>
                  public Memento(string state)
                  {
                      this.state = state;
                  }
           
                  public string GetState()
                  {
                      return this.state;
                  }
           
                  public void SetState(string state)
                  {
                      this.state = state;
                  }
              }
          因為Memento類需要保存Originator對象的狀態,所以Memento具有一個Originator狀態一樣的屬性state,這個state用來保存Originator的一個狀態。
          Memento類的管理者是CareTaker類,CareTaker類的代碼如下。
              /// <summary>
              /// 負責保存備忘錄的管理員
              /// </summary>
              public class CareTaker
              {
                  /// <summary>
                  /// 備忘錄
                  /// </summary>
                  private Memento memento;
           
                  /// <summary>
                  /// 返回所擁有的備忘錄
                  /// </summary>
                  /// <returns></returns>
                  public Memento retrieveMemento()
                  {
                      return this.memento;
                  }
           
                  /// <summary>
                  /// 保存一個備忘錄
                  /// </summary>
                  /// <param name="memento"></param>
                  public void SaveMemento(Memento memento)
                  {
                      this.memento = memento;
                  }
              }
          CareTaker類用來管理Memento對象,所以自身擁有一個Memento對象。并且有一個設置Memento對象方法(SaveMemento)和一個取得Memento的方法(retrieveMemento)。
          備忘錄模式的核心代碼已經定義好了,下面通過客戶端代碼來測試一下備忘錄模式。
                  private static Originator o = new Originator();
                  private static CareTaker c = new CareTaker();
                  public static void Test()
                  {
                      o.SetState("On");
                      c.SaveMemento(o.CreateMemento());
                      Console.WriteLine("第一次為" + o.GetState());
                      o.SetState("Off");
                      Console.WriteLine("第二次為" + o.GetState());
                      o.RestoreMemento(c.retrieveMemento());
                      Console.WriteLine("恢復到上一次為" + o.GetState());
                  }
          在客戶端代碼中,定義了一個Originator的對象o和CareTaker 的對象c,首先對o設置了一個狀態,并且通過c(c是管理備忘錄的)來把o目前的狀態備忘(保存為備忘錄)。然后繼續設置了一下o的狀態,在此時,o想恢復自己的狀態到之前,因為想恢復的狀態保存在了備忘錄中,所以通過o的RestoreMemento可以將狀態恢復為備忘錄所記載的狀態。
          客戶端的測試代碼演示的是備忘錄模式的意圖,而之前的代碼演示的是備忘錄模式的結構。
           
          1.1.3.模型表示
          備忘錄模式的UML類圖如下所示。
          在備忘錄模式的類模型圖中可以看出來,Originator,Memento和CareTaker三者之間的關系,Memento用來保存Originator的一個狀態,Caretaker用來管理Memento。在Originator需要Memento的時候,通過Caretaker即可得到。在Originator需要備忘一個狀態的時候,通過Caretaker即可備忘。
          1.1.4.模式分析
          備忘錄模式就是用來在對象的外部可以保存對象的一個內部狀態。用來存儲另外一個對象內部狀態的快照。備忘錄即對象的一個檢查點,也是一個還原點。
          檢查點:某一個快照所處的位置(即一個狀態)。
          備忘錄模式的意圖是在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態,這樣以后就可以將該對象恢復到原先保存的狀態。
          通過代碼實現中的客戶端測試代碼即可看到備忘錄模式的使用場景:
          1、必須保存一個對象在某一個時刻的狀態,這樣以后需要的時候就能恢復到之前的狀態。
          2、如果用接口來讓其他對象直接得到這些狀態,將會暴露對象的實現細節并破壞對象的封裝性。
           
          通過使用備忘錄模式,可以達到一些效果:
          1、保持封裝邊界。應用使用備忘錄模式,將Originator對象中的一些細節隔離到了Caretaker和Memento中。保持了Originator類內部信息對其他對象的隱藏,從而保持了封裝邊界。
          2、簡化了Originator類,將Originator類中的一些操作交給了Caretaker和Memento。
          3、增加了額外的代價,使用了備忘錄模式,就導致了Originator類中的狀態在產生備忘錄的時候復制了信息,這樣如果需要備忘的信息量很大,那么備忘錄模式會導致額外大量的開銷。
          4、定義窄接口和寬接口。一些語言中可能難以保證只有Originator可訪問備忘錄的狀態。也就是說Originator之外的對象可能會訪問Memento的狀態,導致不安全。
          5、維護備忘錄存在潛在的代價。Caretaker在維護Memento的時候,因為Caretaker不知道備忘錄Memento所保存的狀態數量,所以在通過Caretaker來保存Memento的時候,可能會導致大量的存儲開銷。
           
          因為效果4所說的潛在的安全問題,備忘錄還有另外一種安全的實現方式,也就是將Memento定義為Originator的內部類,這樣的話Memento也就是只有Originator才能訪問了。
          安全模式的一種備忘錄模式代碼實現,如下:
              public interface IMementoSafeMode
              {
              }
          定義一個備忘錄的接口,這樣就封裝了備忘錄的訪問,外部的類都通過訪問接口來訪問備忘錄。備忘錄模式的一個要點就是不破壞封裝性。
          下面是一種安全實現的備忘錄管理員類的實現。
              public class CareTakerSafeMode
              {
                  private IMementoSafeMode memento;
                  public IMementoSafeMode RetriveMemento()
                  {
                      return this.memento;
                  }
                  public void SaveMemento(IMementoSafeMode memento)
                  {
                      this.memento = memento;
                  }
              }
          通過接口的實現,管理員完全不會訪問備忘錄類的,這樣就使得備忘錄類只會暴露給和他有關系的類,這也是單一原則的體現,即迪米特法則(LOD):只與你的朋友通信,不和陌生人說話。
          下面是安全的備忘錄模式的核心實現。
              public class OriginatorSafeMode
              {
                  private string state;
                  public OriginatorSafeMode() { }
           
                  public IMementoSafeMode CreateMemento()
                  {
                       MementoSafeMode mbox = new MementoSafeMode(this.state);
                       return mbox;
                  }
                  public void RestoreMemento(IMementoSafeMode memento)
                  {
                      MementoSafeMode mementoSafeMode = (MementoSafeMode)memento;
                      this.state = mementoSafeMode.State;
                  }
                  public string State
                  {
                      get { return this.state; }
                      set { this.state = value; }
                  }
           
                  protected class MementoSafeMode : IMementoSafeMode
                  {
                      private string state;
                      /// <summary>
                      /// </summary>
                      /// <param name="state"></param>
                      public MementoSafeMode(string state)
                      {
                          this.state = state;
                      }
                      public string State
                      {
                          get { return this.state; }
                          set { this.state = value; }
                      }
                  }
              }
          在之前的備忘錄模式中,備忘錄模式Memento和Originator類是分開的,這里將Memento作為Originator的內部類,這樣限制了Memento的訪問范圍,即只有Originator類的內部才能訪問。
          注意C#和JAVA在處理內部類的時候有不一致的地方。C#中內部類的Public屬性只能是外部類范圍內訪問,如果是private則外部類也無法訪問,只有在內部類之內才能訪問。但是在java中要實現這樣的效果,內部類的所有屬性都必須是Private,這樣才限制了只有外部類訪問。在java中,外部類類似于一個命名空間,通過外部類可以看到內部類(OriginatorSafeMode.MementoSafeMode這樣的寫法就導致了內部類的暴露,只不過無法實例化而已)。即使不能實例化,但是也對外暴露了內部類。在C#中這一點有所改進,內部類在外面根本無法看到。
          實現了內部類的UML類圖省略,內部類在UML中使用圈里面包含的一個叉來顯示,另一端是箭頭,箭頭指向的是內部類。Visual Studio 2010中自帶的UML類圖和Visio中的UML類圖都不支持內部類的表示,使用rose可以畫。
          1.1.5.思想
          目的:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態。這樣以后就可以將該對象恢復到原先保存的狀態。
          實現思想:用一個Memento的類來保存需要恢復的狀態,可以是一個或者多個。并且由另一個類CareTaker來負責恢復。
          1.1.6.應用
          應用中如果有類似歷史記錄機制的功能,那么這個功能有可能就能使用備忘錄模式,因為歷史記錄就是一個過去的狀態,那么如果要訪問歷史記錄就表示要恢復狀態。保存歷史記錄的類就是一個備忘錄類。
          當然了,如果理解了之前的備忘錄模式的實現,那么根據其要點“恢復到原先保存的狀態”,那么如果需要恢復一個類的狀態到以前的某個狀態,則備忘錄基本上就可以搞定,但是,切忌殺雞用牛刀的悲劇。


          -----------------------------------------------------
          Silence, the way to avoid many problems;
          Smile, the way to solve many problems;

          posted on 2012-11-06 09:45 Chan Chen 閱讀(195) 評論(0)  編輯  收藏 所屬分類: Design Pattern

          主站蜘蛛池模板: 江津市| 张掖市| 德保县| 平安县| 曲松县| 天水市| 宣恩县| 洞头县| 凯里市| 霸州市| 长宁区| 浦江县| 胶南市| 广昌县| 临朐县| 曲沃县| 原阳县| 枣阳市| 洪湖市| 武穴市| 苍溪县| 日照市| 阿拉善左旗| 曲水县| 鹤岗市| 英吉沙县| 商洛市| 团风县| 庆阳市| 平原县| 阿克| 青田县| 广安市| 台中市| 合肥市| 临颍县| 万年县| 阳西县| 祁连县| 吉木萨尔县| 麻栗坡县|