2009-02-03 12 views
5

Ich muss eine Geschichte von Zuständen über einige Aktionen in einer Java-Anwendung speichern, die ich später neu laden kann, um den Zustand bei einer bestimmten Aktion wiederherzustellen. Mit anderen Worten, ich habe einen Bildschirm, dem ein Status zugeordnet ist, und ich muss ihn sowie alle Änderungen in einem Verlauf speichern, damit ich den Status des Bildschirms jederzeit wiederherstellen kann. Dies ist eine Art "Rückgängigmachen", aber nicht genau, da der Unterschied zwischen zwei Zuständen sehr groß sein kann und es keine genau definierten Aktionen gibt, die die Zustände ändern.Erstelle Geschichte von Zuständen in Java

Lassen Sie mich mit einem Beispiel erklären: Ein sehr einfacher Bildschirmzustand könnte nur eine einzige Karte enthalten. Im Zustand A enthält diese Karte einen Verweis auf "Object1" mit den Schlüsseln "Key1" und "Object2" mit dem Schlüssel "Key2". Im Zustand B enthält die Map noch den Verweis auf "Object1", aber "Object2" wurde modifiziert und ein "Object3" wurde hinzugefügt. Ich muss jetzt in der Lage sein, zu Zustand A zurückzukehren, was das "Fallenlassen" von Objekt3 und das Wiederherstellen von Objekt2 in seinen vorherigen Zustand beinhalten würde. Ich kann keine benutzerdefinierten "Rückgängig-Aktionen" definieren, da ich nicht weiß, welche Änderungen an Object2 vorgenommen wurden oder was der Typ von Object2 ist. Da die Referenz für Objekt 2 in Zustand A und B gleich bleibt, spiegeln sich diese Änderungen im Zustand A wider, so dass Objekt 2 nicht mehr derselbe ist.

Ich weiß, die beste Lösung ist, Klon-Methoden zu implementieren, aber da ich alle Arten von Objekten (einschließlich Primitiven und Standardsammlungen) unterstützen muss, ist dies nicht machbar. Ich habe über serializable nachgedacht, wo ich die Map serialisieren würde, sobald ein Zustandsübergang stattfindet, und sie dann deserialisieren würde, wenn sie wieder benötigt wird, aber es scheint eine sehr hässliche Lösung zu sein.

Hat jemand noch andere Ideen? Vielen Dank, Ristretto

+0

Du hast gesagt, du musst alle Arten von Objekten und Primitiven unterstützen .... Dann sagst du, dass du daran denkst, die Karte zu serialisieren. ... nicht richtig verstanden. –

Antwort

0

Wir machen etwas ähnliches mit Serialisierung.

Wir speichern archivierte Daten in serialisierter Form im Dateisystem. Der Teil des Objektdiagramms, den wir wiederherstellen müssen, ist sowohl serialisiert als auch das Hauptobjekt.

Stellen Sie sicher, dass Sie Ihre Objekte versionieren und sicherstellen, dass Ihre Differenzierung mit fehlenden/neuen Feldern umgehen kann.

Wir haben uns entschieden, das Dateisystem zu speichern, weil es uns (effektiv) unbegrenzte Kapazität gibt. Die Geschwindigkeit ist kein Problem für uns, aber die Dateisystem-Methode ist überraschend schnell, die meisten Leute bemerken die zusätzlichen 50-100ms nicht!

0

Sie könnten immer Serialisierung verwenden;

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); 
objectOutputStream.writeObject(object); 
objectOutputStream.flush(); 
byteArrayOutputStream.close(); 
ByteArrayInputStream istream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); 
ObjectInputStream objectInputStream= new ObjectInputStream(istream); 
Object deserialized = objectInputStream.readObject(); 
istream.close(); 

Langsam und klobig, aber funktioniert.

12

Haben Sie versucht, in die Memento Design Pattern zu schauen? Es scheint für Ihr Problem besonders gut definiert zu sein. Aus Wikipedia:

Das Erinnerungsmuster ist ein Software Entwurfsmuster, das die Fähigkeit stellt ein Objekt in seine vorherigen Zustand (zurücksetzen über Rollback) wiederherzustellen.

Die gleiche Seite hat auch einen Abschnitt mit einer Java implementation seit Sie erwähnt, dass dies in Java ist.

+0

Großartige Verbindung. Irgendwelche Vorschläge, wie Sie diese für Objekte mit komplexeren Status verwenden können (das Beispiel verwendet nur Strings) - Serialisierung? –

0

Wenn das, was Sie wollen in der Tat ist eine Karte, möchten Sie vielleicht in persistente Datenstrukturen suchen; zum Beispiel, persistente B-Bäume.

1

Betrachten Sie einen Perspektivenwechsel. Anstatt die Objekte zu verändern, die den Status des Bildschirms ausmachen, verwenden Sie einen unveränderlichen Status. Das klingt nach einem Widerspruch in sich, ist es aber nicht.

Sagen Sie zum Beispiel (der Einfachheit halber), dass Ihr Zustand aus einem einzigen String besteht. Da Strings unveränderbar sind, müssen Sie die Zeichenfolge natürlich nicht klonen, um den Status zu speichern und zu ändern. Zum Beispiel:

public List<String> changeTheScreen(List<String> states) { 
    return states.cons(states.head() + "x"); 
} 

public void renderTheScreen(String currentState) { 
    // TODO: draw the screen given the current state 
} 

In dem obigen Beispiel ist Listfj.data.List, ein unveränderliche In-Memory-Liste Typen aus den Functional Java bibliotheks einzeln verknüpft (die Standardbibliotheken nicht eine unveränderliche Liste haben). Die Methode würde die Geschichte von Staaten mit dem aktuellen Zustand an der Spitze der Liste nehmen. Es manipuliert den Zustand des Bildschirms, indem es einen neuen Zustand erstellt und ihn an die Spitze einer neuen Liste von Zuständen setzt.

Wenden Sie dieses Prinzip auf jeden Typ an, den Sie als Status verwenden möchten. Stellen Sie sicher, dass Ihr Zustand vollständig aus unveränderlichen Objekten besteht (Strings und Primitive sind bereits unveränderlich). Die Verwendung von unveränderlichen Objekten für den Status wird Ihnen eine Menge Wartungskopfschmerzen auf der Straße ersparen und Speicher sparen, da unveränderliche Dinge wiederverwendet werden können, ohne geklont werden zu müssen.

Ein unveränderliches Objekt wird in seinem Konstruktor initialisiert und alle seine inneren Felder werden final sein.

Functional Java has an immutable map called TreeMap. Sie würden es auf folgende Weise verwenden:

public List<TreeMap<String, Object>> 
changeState(List<TreeMap<String, Object>> states) { 
    return states.cons(states.head().set("Key1", new Object1("x"))); 
} 
0

In meinem Projekt, wir etwas sehr ähnliches durch die Serialisierung in XML-Dateien erreicht. Das hat gut für uns funktioniert. Alle Objekte, die Sie zurückerhalten möchten - Serialisieren Sie in einer XML-Datei auf eine wohldefinierte Art und Weise, so dass Sie jederzeit später den Status aus der XML-Datei abrufen können.