Chan Chen Coding...

          Eleven: Memento Design Pattern

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


          -----------------------------------------------------
          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

          主站蜘蛛池模板: 保定市| 普定县| 东源县| 饶河县| 拜泉县| 得荣县| 承德县| 大渡口区| 桂平市| 延长县| 抚松县| 乌拉特中旗| 蓬溪县| 榆林市| 屏边| 香格里拉县| 抚远县| 宜兴市| 北票市| 汾西县| 隆林| 云阳县| 昆山市| 宝坻区| 邻水| 肥城市| 若羌县| 皋兰县| 衡山县| 深州市| 合山市| 长治县| 长沙市| 西乡县| 专栏| 安徽省| 湖北省| 东乡| 逊克县| 方正县| 安仁县|