Как реализуется шаблон Memento в C#4?

на Memento Pattern сам по себе кажется довольно прямым. Я рассматриваю возможность реализации того же, что и пример Википедии, но прежде чем я это сделаю, есть ли какие-либо языковые функции C#, которые облегчают реализацию или использование?

4 ответов


одной очевидной особенностью будут дженерики, реализация общего memento позволит вам использовать его для любого объекта, который вы хотите.

многие примеры, которые вы увидите, будут использовать строку (включая все те, которые в настоящее время среди ответов на этот вопрос) как состояние, которое является проблемой, поскольку это один из немногих типов в .NET, которые являются неизменяемые.

при общении с mutable объекты (как и любой ссылочный тип с свойством setter) у вас есть чтобы помнить, что при сохранении memento вам нужно создать deepcopy объекта. В противном случае всякий раз, когда вы меняете свой исходный объект, вы будете менять свой сувенир.

вы можете сделать это с помощью сериализатора как protobuf-чистая или json.net поскольку они не требуют, чтобы вы отмечали свои объекты с помощью сериализуемого атрибута, как это делает обычный механизм сериализации .net.

У Codeproject есть несколько статей о generic memento реализации, но они, как правило, пропускают часть deepcopy:

общий шаблон Memento для отмены повтора в C#

Шаблон Дизайна Memento


Я не знаю ни одного уже встроенного способа поддержки Memento узор. Я вижу пару реализаций, используя .NET Mock Framework, где на практике создается клон объекта и может быть поле с данными, но я считаю это своего рода накладными расходами.

использование Memento patter на Undo / Redo обычно, вероятно, вы тоже. В этом случае лучше иметь как можно меньше данных о стеке отмены / повтора, поэтому пользовательский undoable object это то, что Я пошел бы на.

надеюсь, что это помогает.


есть одна вещь, которая сделает этот шаблон немного быстрее писать на 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]);   
    }
}

Я нашел один, используя дженерики здесь:

#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 упоминает, это будет работать только для неизменяемых данных и требует глубокая копия.