2012-01-24 15 views

risposta

15

Una caratteristica ovvia sarebbe generici, l'implementazione di un ricordo generico ti consentirà di utilizzarlo per qualsiasi oggetto desideri.

Molti esempi che vedrete utilizzeranno una stringa (compresi tutti quelli attualmente tra le risposte a questa domanda) come stato che è un problema poiché è uno dei pochi tipi in .NET che sono immutable.

Quando si tratta di oggetti mutabili (come qualsiasi tipo di riferimento con una proprietà di setter) è necessario ricordare che quando si salva il ricordo è necessario creare una copia dell'oggetto. Altrimenti ogni volta che cambi l'oggetto originale cambierai il tuo ricordo.

È possibile eseguire questa operazione utilizzando un serializzatore come protobuf-net o json.net poiché non richiedono di contrassegnare gli oggetti con attributo serializzabile come fa il normale meccanismo di serializzazione .net.

CodeProject hanno pochi articoli su implementazioni Memento generici, ma tendono a saltare la parte deepcopy:

Generic Memento Pattern for Undo-Redo in C#

Memento Design Pattern

+0

È possibile aggiungere ulteriori informazioni sulla parte di copia profonda? Un link ad un esempio di questa parte dell'implementazione o qualcosa del genere. Ottima risposta quindi grazie ... – MoonKnight

+0

Appena visto il link sottostante. Grazie. – MoonKnight

3

Non sono a conoscenza di alcun modo già integrato per supportare il modello Memento. Vedo un paio di implementazioni usando .NET Mock frameworks, dove in pratica viene creato un clone dell'oggetto e può essere campo con un dato, ma lo considero un po 'sovraccarico.

L'uso Memento patter su Annulla/Ripristina di solito, probabilmente anche tu. In questo caso, è meglio avere il minor numero possibile di dati sullo stack Undo/Redo, quindi l'undoable object personalizzato è qualcosa che sarebbe I.

Spero che questo aiuti.

2

C'è una cosa che renderà questo schema leggermente più veloce da scrivere in C# e cioè che tutti i campi di stato possono essere dichiarati come public readonly in modo da non aver bisogno di proprietà o metodi 'get' per accedervi.

Questa è una conversione diretta con public readonly inclusa.

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

Sei sicuro costruttore privato corretto per la classe Memento? è accessibile da Originator? –

+0

Hai ragione! Non ho compilato questo o qualcosa di geez. Ora è stato risolto grazie. –

1

uno utilizzando Generics ho trovato 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 

si usa così:

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

Come @Simon Skov Boisen menzioni questo funziona solo per i dati immutabili e richiede un deep copy.

+2

Vedere la mia risposta, è necessario prendere in considerazione deepcopying quando si tratta di tipi di riferimento con proprietà setter. –

Problemi correlati