【问题标题】:How is the Memento Pattern implemented in C#4?C#4 中的备忘录模式是如何实现的?
【发布时间】:2012-02-18 03:33:36
【问题描述】:

Memento Pattern 本身看起来很简单。我正在考虑实现与 wikipedia 示例相同的方法,但在此之前,是否有 C# 的任何语言功能使其更易于实现或使用?

【问题讨论】:

    标签: c# design-patterns memento


    【解决方案1】:

    我不知道有任何支持Memento 模式的内置方法。 我通过使用.NET Mock frameworks 看到了几个实现,实际上创建了对象的克隆并且可以是带有数据的字段,但我认为这是一种开销。

    通常在 Undo/Redo 上使用 Memento 模式,可能你也是。在这种情况下,最好在 Undo/Redo 堆栈上保留尽可能少的数据,因此 I 会采用自定义 undoable object

    希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      有一件事会使这种模式在 C# 中编写得稍微快一点,那就是任何状态字段都可以声明为public readonly,因此您不需要属性或“get”方法来访问它们。

      这是包含public readonly 的直接转换。

      class Originator 
      {
          private string state;
          // The class could also contain additional data that is not part of the
          // state saved in the memento.
      
          public void Set(string state) 
          {
              Console.WriteLine("Originator: Setting state to " + state);
              this.state = state;
          }
      
          public Memento SaveToMemento() 
          {
              Console.WriteLine("Originator: Saving to Memento.");
              return new Memento(state);
          }
      
          public void RestoreFromMemento(Memento memento) 
          {
              state = memento.SavedState;
              Console.WriteLine("Originator: State after restoring from Memento: " + state);
          }
      
          public class Memento 
          {
              public readonly string SavedState;
      
              public Memento(string stateToSave)  
              {
                  SavedState = stateToSave;
              }
          }
      }
      
      class Caretaker 
      {
          static void Main(string[] args) 
          {
              List<Originator.Memento> savedStates = new List<Originator.Memento>();
      
              Originator originator = new Originator();
              originator.Set("State1");
              originator.Set("State2");
              savedStates.Add(originator.SaveToMemento());
              originator.Set("State3");
              // We can request multiple mementos, and choose which one to roll back to.
              savedStates.Add(originator.SaveToMemento());
              originator.Set("State4");
      
              originator.RestoreFromMemento(savedStates[1]);   
          }
      }
      

      【讨论】:

      • 你确定私有构造函数对 Memento 类正确吗?是否可以从 Originator 访问?
      • 你是对的!我没有编译这个还是什么天啊。现在已修复,谢谢。
      【解决方案3】:

      我找到了一个使用泛型here

      #region Originator
      public class Originator<T>
      {
         #region Properties
         public T State { get; set; }
         #endregion
         #region Methods
         /// <summary>
         /// Creates a new memento to hold the current
         /// state
         /// </summary>
         /// <returns>The created memento</returns>
         public Memento<T> SaveMemento()
         {
            return (new Memento<T>(State));
         }
         /// <summary>
         /// Restores the state which is saved in the given memento
         /// </summary>
         /// <param name="memento">The given memento</param>
         public void RestoreMemento(Memento<T> memento)
         {
            State = memento.State;
         }
         #endregion
      }
      #endregion
      #region Memento
      public class Memento<T>
      {
         #region Properties
         public T State { get; private set; }
         #endregion
         #region Ctor
         /// <summary>
         /// Construct a new memento object with the
         /// given state
         /// </summary>
         /// <param name="state">The given state</param>
         public Memento(T state)
         {
            State = state;
         }
         #endregion
      }
      #endregion
      #region Caretaker
      public class Caretaker<T>
      {
         #region Properties
         public Memento<T> Memento { get; set; }
         #endregion
      }
      #endregion
      #region Originator
      public class Originator<T>
      {
         #region Properties
         public T State { get; set; }
         #endregion
         #region Methods
         /// <summary>
         /// Creates a new memento to hold the current
         /// state
         /// </summary>
         /// <returns>The created memento</returns>
         public Memento<T> SaveMemento()
         {
            return (new Memento<T>(State));
         }
         /// <summary>
         /// Restores the state which is saved in the given memento
         /// </summary>
         /// <param name="memento">The given memento</param>
         public void RestoreMemento(Memento<T> memento)
         {
            State = memento.State;
         }
         #endregion
      }
      #endregion
      #region Memento
      public class Memento<T>
      {
         #region Properties
         public T State { get; private set; }
         #endregion
         #region Ctor
         /// <summary>
         /// Construct a new memento object with the
         /// given state
         /// </summary>
         /// <param name="state">The given state</param>
         public Memento(T state)
         {
            State = state;
         }
         #endregion
      }
      #endregion
      #region Caretaker
      public class Caretaker<T>
      {
         #region Properties
         public Memento<T> Memento { get; set; }
         #endregion
      }
      #endregion
      

      这样使用:

         Originator<string> org = new Originator<string>();
         org.State = "Old State";
         // Store internal state in the caretaker object
         Caretaker<string> caretaker = new Caretaker<string>();
         caretaker.Memento = org.SaveMemento();
         Console.WriteLine("This is the old state: {0}", org.State);
         org.State = "New state";
         Console.WriteLine("This is the new state: {0}", org.State);
         // Restore saved state from the caretaker
         org.RestoreMemento(caretaker.Memento);
         Console.WriteLine("Old state was restored: {0}", org.State);
         // Wait for user
         Console.Read();
      

      正如@Simon Skov Boisen 所说,这仅适用于不可变数据并且需要deep copy

      【讨论】:

      • 看我的回答,在处理带有 setter 属性的引用类型时,您必须考虑 deepcopying。
      【解决方案4】:

      一个明显的特性是泛型,实现一个泛型纪念品将允许您将它用于您想要的任何对象。

      您将看到的许多示例将使用字符串(包括当前对该问题的答复中的所有字符串)作为状态,这是一个问题,因为它是 .NET 中为数不多的类型之一,不可变

      在处理 可变 对象时(就像任何具有 setter 属性的引用类型),您必须记住,当您保存备忘录时,您需要创建对象的深层副本。否则,每当您更改原始对象时,您都会更改您的纪念品。

      您可以通过使用像protobuf-netjson.net 这样的序列化程序来做到这一点,因为它们不需要像普通的.net 序列化机制那样使用可序列化属性标记您的对象。

      Codeproject 关于通用纪念品实现的文章很少,但他们倾向于跳过 deepcopy 部分:

      Generic Memento Pattern for Undo-Redo in C#

      Memento Design Pattern

      【讨论】:

      • 你能补充更多关于深拷贝部分的信息吗?指向这部分实现或其他内容的示例的链接。很好的答案,谢谢...
      • 刚刚看到下面的链接。谢谢。
      猜你喜欢
      • 2018-02-02
      • 1970-01-01
      • 2021-01-22
      • 2011-01-06
      • 2023-03-31
      • 2013-12-01
      • 2017-06-28
      • 2012-07-09
      • 2014-07-22
      相关资源
      最近更新 更多