Memento Pattern выглядит довольно прямолинейно. Я рассматриваю возможность реализовать то же, что и в примере с википедией, но прежде, чем я увижу какие-либо языковые возможности С#, которые упрощают реализацию или использование?
Как шаблон Memento реализован в С# 4?
Ответ 1
Одна очевидная особенность - это дженерики, реализация универсального приложения позволит вам использовать его для любого объекта, который вы хотите.
Многие примеры, которые вы увидите, будут использовать строку (включая все, что входит в число ответов на этот вопрос) как состояние, которое является проблемой, поскольку это один из немногих типов .NET, которые являются неизменяемыми.
При работе с изменяемыми объектами (например, любым ссылочным типом с использованием свойства setter) вы должны помнить, что при сохранении памяти вам нужно создать глубокую копию объекта. В противном случае, когда вы меняете оригинальный объект, вы меняете свое воспоминание.
Вы можете сделать это, используя сериализатор, например protobuf-net или json.net, так как они не требуют, чтобы вы отмечали свои объекты с помощью сериализуемого атрибута, как это делает обычный механизм сериализации .net.
Codeproject содержит несколько статей об общих реализациях рецептов, но они, как правило, пропускают часть глубокой копии:
Ответ 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 упоминает, что это будет работать только для неизменяемых данных и требует глубокой копии.