Подтвердить что ты не робот

Как шаблон Memento реализован в С# 4?

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

4b9b3361

Ответ 1

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

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

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

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

Codeproject содержит несколько статей об общих реализациях рецептов, но они, как правило, пропускают часть глубокой копии:

Общий шаблон Memento для Undo-Redo в С#

Memento Design Pattern

Ответ 2

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

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

Надеюсь, что это поможет.

Ответ 3

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

Ответ 4

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

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