Memento パターン自体はかなり単純明快に思えます。ウィキペディアの例と同じものを実装することを検討していますが、その前に、実装または使用を容易にする C# の言語機能はありますか?
4 に答える
明らかな機能の 1 つはジェネリックです。ジェネリックのメメントを実装すると、必要な任意のオブジェクトに使用できるようになります。
目にする多くの例では、文字列 (この質問に対する現在の回答の中にあるすべてのものを含む) を状態として使用しますが、これは .NET でimmutableである数少ない型の 1 つであるため、問題になります。
変更可能なオブジェクト (セッター プロパティを持つ参照型など)を扱う場合は、メモを保存するときにオブジェクトのディープコピーを作成する必要があることを覚えておく必要があります。そうしないと、元のオブジェクトを変更するたびに、記念品が変更されます。
protobuf-netやjson.netなどのシリアライザーを使用してこれを行うことができます。これは、通常の .net シリアライゼーション メカニズムのようにオブジェクトをシリアライズ可能な属性でマークする必要がないためです。
Codeproject には一般的な memento の実装に関する記事はほとんどありませんが、deepcopy の部分をスキップする傾向があります。
Memento
パターンをサポートする組み込みの方法については知りません。.NET モック フレームワークを使用した実装がいくつか見られます。実際には、オブジェクトのクローンが作成され、データを含むフィールドにすることができますが、これは一種のオーバーヘッドだと考えています。
Memento
通常、Undo/Redo でパターンを使用するのは、おそらくあなたも同じでしょう。この場合、Undo/Redo スタックのデータをできるだけ少なくする方がよいため、カスタムundoable object
は私が求めるものです。
お役に立てれば。
このパターンを C# で書くのが少し速くなる 1 つのことがあります。それは、どの状態フィールドも宣言できるpublic readonly
ので、それらにアクセスするためのプロパティや「get」メソッドは必要ありません。
public readonly
これは、 includedを使用した直接的な変換です。
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]);
}
}
ここで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 が言及しているように、これは不変データに対してのみ機能し、ディープ コピーが必要です。