16

Memento Pattern本身看起来很简单。我正在考虑实现与维基百科示例相同的方法,但在我这样做之前,是否有 C# 的任何语言特性使其更易于实现或使用?

4

4 回答 4

17

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

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

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

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

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

C# 中用于撤销-重做的通用备忘录模式

纪念品设计模式

于 2012-01-24T21:51:04.460 回答
3

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

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

希望这可以帮助。

于 2012-01-24T21:36:06.303 回答
2

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

这是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]);   
    }
}
于 2012-01-24T21:39:11.073 回答
1

我在这里找到了一个使用泛型

#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 提到的,这仅适用于不可变数据并且需要深拷贝

于 2012-01-24T21:48:48.187 回答