Design Patterns: Memento
Nachdem ich hier bereits den Singleton beschrieben habe, folgt nun ein weiterer Design Pattern (Entwurfsmuster), diesmal allerdings aus dem Bereich der Behavioral Patterns (Verhaltensmustern). Der sogenannte Memento, oder auch als Token bezeichnet, soll den internen Zustand eines Objektes erfassen und diesen nach außen verlagern können, ohne dabei die Kapselung dieses Objektes zu beschädigen.
Der Memento sollte immer dann zum Einsatz kommen, wenn ein aktueller Zustand eines Objektes zwischengespeichert werden soll oder aber eine direkte Schnittstelle zur Ermittlung dieses Zustandes Details zur Implementierung aufzeigen würde. Letzteres kann ebenfalls mit dem Memento Pattern verhindert werden.

Prinzipiell ist der Memento nur eine Vereinigung von Serialize und Deserialize Methoden in C# mit der Möglichkeit das Objekt so zwischen zu speichern. Wir benötigen hierzu also zunächst einmal eine Klasse die wir serialisieren können.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | [Serializable] class KundenKonto { private string _vorname; private string _nachname; private string _kontonummer; private double _guthaben; public string Vorname { get { return _vorname; } set { _vorname = value; } } public string Nachname { get { return _nachname; } set { _nachname = value; } } public string Kontonummer { get { return _kontonummer; } set { _kontonummer = value; } } public double Guthaben { get { return _guthaben; } set { _guthaben = value; } } // Dient zum Setzen (Serialisieren) des Memento public Memento SaveMemento() { var memento = new Memento(); return memento.Serialize(this); } // Dient zum Zurücksetzen (Deserialisieren) des Memento public void RestoreMemento(Memento memento) { KundenKonto konto = (KundenKonto)memento.Deserialize(); this.Vorname = konto.Vorname; this.Nachname = konto.Nachname; this.Kontonummer = konto.Kontonummer; this.Guthaben = konto.Guthaben; } } |
[Serializable]
class KundenKonto
{
private string _vorname;
private string _nachname;
private string _kontonummer;
private double _guthaben;
public string Vorname { get { return _vorname; } set { _vorname = value; } }
public string Nachname { get { return _nachname; } set { _nachname = value; } }
public string Kontonummer { get { return _kontonummer; } set { _kontonummer = value; } }
public double Guthaben { get { return _guthaben; } set { _guthaben = value; } }
// Dient zum Setzen (Serialisieren) des Memento
public Memento SaveMemento()
{
var memento = new Memento();
return memento.Serialize(this);
}
// Dient zum Zurücksetzen (Deserialisieren) des Memento
public void RestoreMemento(Memento memento)
{
KundenKonto konto = (KundenKonto)memento.Deserialize();
this.Vorname = konto.Vorname;
this.Nachname = konto.Nachname;
this.Kontonummer = konto.Kontonummer;
this.Guthaben = konto.Guthaben;
}
}Wie man nun sieht, haben wir eine Klasse, die Informationen zum Konto sowie zwei Methoden zum Speichern und Zurücksetzen des Objektes enthält. Diese zwei Methoden erfordern allerdings ein Objekt vom Typ Memento, welches wir in folgender Klasse definieren.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Memento { private MemoryStream _stream = new MemoryStream(); private SoapFormatter _formatter = new SoapFormatter(); // Serialisieren public Memento Serialize(object mem) { _formatter.Serialize(_stream, mem); return this; } // Deserialisieren public object Deserialize() { _stream.Seek(0, SeekOrigin.Begin); object mem = _formatter.Deserialize(_stream); _stream.Close(); return mem; } } |
class Memento
{
private MemoryStream _stream = new MemoryStream();
private SoapFormatter _formatter = new SoapFormatter();
// Serialisieren
public Memento Serialize(object mem)
{
_formatter.Serialize(_stream, mem);
return this;
}
// Deserialisieren
public object Deserialize()
{
_stream.Seek(0, SeekOrigin.Begin);
object mem = _formatter.Deserialize(_stream);
_stream.Close();
return mem;
}
}Diese Klasse erlaubt es uns nun, sämtliche serialisierbaren Objekte in ihrem aktuellen Zustand zu speichern. Erforderlich ist lediglich die korrekte Implementierung der SaveMemento() und RestoreMemento() Methoden in den jeweiligen Klassen und diese mit [Serializable] zu kennzeichnen. Zusätzlich brauchen wir noch eine sogenannte Caretaker Klasse, in die wir den Memento zwischenspeichern können. Aufgebaut ist sie wie folgt:
1 2 3 4 | class KundenKontoMemory { public Memento Memento { get; set; } } |
class KundenKontoMemory
{
public Memento Memento { get; set; }
}Im Ergebnis können wir nun in unserer Main() Methode das Objekt initialisieren und mit den Methoden SaveMemento() im KundenKontoMemory speichern bzw. mit RestoreMemento() aus dem KundenKontoMemory zurückholen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | static void Main() { // Initialisierung var meinKonto = new KundenKonto { Vorname = "Max", Nachname = "Mustermann", Kontonummer = "123456789", Guthaben = 25000.0 }; // Memento erstellen und sichern var backupKonto = new KundenKontoMemory(); backupKonto.Memento = meinKonto.SaveMemento(); // Daten manipulieren meinKonto.Vorname = "Testi"; meinKonto.Nachname = "Testmann"; meinKonto.Kontonummer = "987654321"; meinKonto.Guthaben = 1000; // Memento rücksichern meinKonto.RestoreMemento(backupKonto.Memento); } |
static void Main()
{
// Initialisierung
var meinKonto = new KundenKonto
{
Vorname = "Max",
Nachname = "Mustermann",
Kontonummer = "123456789",
Guthaben = 25000.0
};
// Memento erstellen und sichern
var backupKonto = new KundenKontoMemory();
backupKonto.Memento = meinKonto.SaveMemento();
// Daten manipulieren
meinKonto.Vorname = "Testi";
meinKonto.Nachname = "Testmann";
meinKonto.Kontonummer = "987654321";
meinKonto.Guthaben = 1000;
// Memento rücksichern
meinKonto.RestoreMemento(backupKonto.Memento);
}So haben wir unser Objekt nun veräußern, also zwischenspeichern können, ohne die Kapselung des Objektes an sich zu beschädigen. Objekte können so auch ohne Weiteres zwischen Applikationen ausgetauscht werden, da die serialisierten Objekte zusätzlich noch komprimiert werden können.
Der oben beschriebene Code steht jedem zur freien Verwendung zur Verfügung, ich wünsche viel Spaß!