Как шаблон Memento реализован в C # 4? - PullRequest
16 голосов
/ 25 января 2012

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

Ответы [ 4 ]

15 голосов
/ 25 января 2012

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

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

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

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

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

Универсальный шаблон мементо для Undo-Redo в C #

Шаблон дизайна сувенира

3 голосов
/ 25 января 2012

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

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

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

2 голосов
/ 25 января 2012

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

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

...