patron observer c# design-patterns memento

observer - ¿Cómo se implementa el Patrón Memento en C#4?



patron memento c# (4)

El patrón de recuerdo en sí parece bastante sencillo. Estoy considerando implementar lo mismo que en el ejemplo de wikipedia, pero antes de hacerlo, ¿hay alguna característica de lenguaje de C # que facilite su implementación o uso?


Hay una cosa que hará que este patrón sea un poco más rápido de escribir en C # y es que cualquier campo de estado se puede declarar como public readonly para que no necesite propiedades ni métodos de "obtención" para acceder a ellos.

Aquí hay una conversión directa con public readonly incluida.

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]); } }


He encontrado uno usando genéricos here :

#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

Utilizado de esta manera:

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();

Como @Simon Skov Boisen menciona, esto solo funcionará para datos inmutables y requiere una copia profunda .


Una característica obvia sería los genéricos, la implementación de un recuerdo genérico le permitirá utilizarlo para cualquier objeto que desee.

Muchos de los ejemplos que verá usarán una cadena (incluidas todas las respuestas actuales a esta pregunta) como estado, lo cual es un problema, ya que es uno de los pocos tipos de .NET que son inmutables .

Al tratar con objetos mutables (como cualquier tipo de referencia con una propiedad de establecimiento), debe recordar que al guardar el recuerdo debe crear una copia en profundidad del objeto. De lo contrario, cada vez que cambie su objeto original, cambiará su recuerdo.

Puede hacer esto utilizando un serializador como protobuf-net o json.net ya que no requieren que marque sus objetos con un atributo serializable como lo hace el mecanismo de serialización .net normal.

Codeproject tiene pocos artículos sobre implementaciones de memento genérico, pero tienden a omitir la parte de copia profunda:

Patrón de recuerdo genérico para deshacer y rehacer en C #

Patrón de diseño de recuerdo


No estoy al tanto de ninguna forma ya incorporada para admitir el patrón Memento . Veo un par de implementaciones utilizando .NET Mock frameworks , donde en la práctica se crea un clon del objeto y puede ser un campo con datos, pero lo considero una especie de sobrecarga.

El uso del patrón Memento en Deshacer / Rehacer por lo general, probablemente usted también. En este caso, es mejor tener la menor cantidad de datos posible en la pila de Deshacer / Rehacer, por lo que el undoable object personalizado que se puede undoable object es algo a lo que me gustaría ir.

Espero que esto ayude.