2010-07-29 12 views
8

Ich bin auf der Suche nach einem Objekt, das aufgefunden wird oder eine persistente Transaktion enthält. Das heißt, das Objekt enthält Daten (möglicherweise eine Map). Wenn Änderungen an den Daten vorgenommen werden, werden diese Änderungen separat gespeichert, je nach Bedarf, sodass jedes externe Objekt entweder auf den Basiszustand verweisen kann (vor den Änderungen) oder auf die neuesten Daten zugreifen kann. Dann gibt es eine andere Operation, die die Änderungen in den Basiszustand festschreibt."Journaling" oder "Transaktionen" Design-Muster?

Es erinnert mich etwas an das Linux-Journaling-Dateisystem. Dateisystemänderungen werden in ein Journal geschrieben und erst später in den permanenten Speicher übernommen.

Es ähnelt vielleicht auch dem Konzept einer "Transaktion" in der relationalen Datenbank Welt; das heißt, Sie haben einige Daten, Sie beginnen eine Transaktion und manipulieren die Daten in irgendeiner Weise. Gleichzeitige Prozesse sehen die alten Daten mit keiner Ihrer Änderungen. Dann können Sie entweder die Transaktion "zurücksetzen" oder Ihre Änderungen "committen".

Ich bin speziell auf der Suche nach dies in Java zu implementieren, aber offensichtlich ist es ein allgemeines objektorientiertes Muster, wenn es überhaupt existiert. Ich hoffe, dass es zumindest erstellt werden kann, aber ich bin mir nicht sicher, wie ich es am besten umsetzen könnte.

Angenommen, das Objekt enthält eine ganze Tonne Daten, eine ganze Hierarchie (Unterobjekte usw.). Man kann also nicht einfach zwei Kopien des gesamten Datenbaums behalten; es wäre sehr speicherverschwendend und die Kopieroperation (beim Festschreiben) würde viel zu lange dauern. Ich versuche, dies im Kontext eines Spiels zu implementieren, mit einer Commit-Operation pro Frame, also muss es wirklich optimal sein.

Antwort

7

Der beste Weg, um zu erreichen, was Sie wollen, ist, das Objekt und alle seine Unterobjekte unveränderlich zu machen. Dann können die beiden Threads ohne Konflikte mit ihnen arbeiten, und Sie müssen nicht zwei Kopien von allem verwalten. Die einzigen Dinge, die zwei Kopien erfordern, sind die Dinge, die sich tatsächlich ändern, und diese können sehr klein sein.

Angenommen, Objekt A besteht aus den Objekten B und C. Objekt B besteht aus den Objekten D und E. Das Objekt C besteht aus den Objekten F und G. Also sind A, B und C jeweils nur zwei Zeiger und D, E, F und G sind, was auch immer sie sind.

Zuerst erstellen Sie Ihre erste Instanz und geben sie an beide Threads weiter.

ThreadOne -> A1{ B1{ D1, E1 } C1{ F1, G1 } } 
ThreadTwo -> A1{ B1{ D1, E1 } C1{ F1, G1 } } 

So beiden Fäden in der gleichen Instanz zeigen, wird kein zusätzlicher Speicher verbraucht, und es gibt keine Threadingprobleme, da die Objekte ändern sie nicht immer tun.

Jetzt muss ThreadOne das Objekt F ändern. Dazu erstellt es einfach ein neues F, ein neues C, um es zu enthalten, und ein neues A, um das neue C zu enthalten. Das Original B, D, E und G sind unverändert und müssen nicht kopiert werden.

ThreadOne -> A2{ B1{ D1, E1 } C2{ F2, G1 } } 
ThreadTwo -> A1{ B1{ D1, E1 } C1{ F1, G1 } } 

Die zwei Threads, die Instanzen von B teilen, D, E und G.

Jetzt ThreadOne Objekt E.

ThreadOne -> A3{ B2{ D1, E2 } C2{ F2, G1 } } 
ThreadTwo -> A1{ B1{ D1, E1 } C1{ F1, G1 } } 

Jetzt ThreadTwo muss die neueste Version ändern muss, also gibt ThreadOne nur einen Zeiger auf seine Kopie.

ThreadOne -> A3{ B2{ D1, E2 } C2{ F2, G1 } } 
ThreadTwo -> A3{ B2{ D1, E2 } C2{ F2, G1 } } 

Da die Objekte unveränderlich sind, besteht keine Gefahr einer Threadingprobleme und ThreadOne können gehen Recht vor, Änderungen an machen, jedes Mal eine neue Instanz nur die Teile zu schaffen, die sich geändert haben, und deren Behälter.

ThreadOne -> A4{ B3{ D2, E2 } C2{ F2, G1 } } 
ThreadTwo -> A3{ B2{ D1, E2 } C2{ F2, G1 } } 

ThreadOne -> A5{ B3{ D2, E2 } C3{ F3, G1 } } 
ThreadTwo -> A3{ B2{ D1, E2 } C2{ F2, G1 } } 

Dies ist schnell, speichereffizient und threadsicher.

+1

Wow, das ist eigentlich wirklich cool und ich habe bis jetzt nie wirklich in unveränderbare Klassen geschaut. Sie haben definitiv positive Ergebnisse und ich denke, dass dies die beste Lösung für dieses Problem sein könnte! Der einzige Nachteil, den ich sehe, ist die Langweiligkeit, Elternobjekte jedes Mal neu zu erstellen, wenn sich ein Kindobjekt ändert, aber für die anderen Vorteile denke ich, dass ich damit zurechtkommen kann. – Ricket

1

Was Sie suchen, ist die Command Pattern. Sie erstellen Befehlsobjekte, die die Änderungen ausführen und rückgängig machen, und speichern nur einige Basiszustände und wiederholen dann alle Befehle, die benötigt werden, um das Objekt in den gewünschten Zustand zu bringen.

+0

Nicht ganz. Ich brauche den Grundzustand und die neuesten Zustände, um zufällig zugänglich zu sein; Ich werde zwei Threads haben, einen, der vom Grundzustand funktioniert, und den anderen, der vom letzten Zustand arbeitet. Mit dem Befehlsmuster würde ich das Objekt grundsätzlich hin- und herdrehen (Rückgängigmachen und Wiederholen) und es wäre SEHR ineffizient, wie Sie sich sicherlich vorstellen können. – Ricket

+0

dann wiederholen Sie vom Basiszustand in den Zustand, den Sie "zufällig" benötigen, sobald Sie einen bestimmten Punkt wiedergeben, Sie "Rebase" oder "Snapshot" welcher Status Sie ausführen müssen und verwenden Sie diese als Ihre Basis und von dort wieder zu spielen . Dies ist das "Standard" -Muster für das, wonach Sie suchen. Wenn Sie nicht versucht haben, es zu implementieren, wissen Sie nicht, wie es wird. So funktionieren alle Journaling-Systeme, dafür gibt es keinen magischen Algorithmus. Der Befehl ist im Wesentlichen Deltas vom vorherigen Zustand. –

1

Was Sie beschreiben, klingt sehr ähnlich wie die Memento pattern. Sie haben jedoch angegeben, dass Sie den gesamten Status des Objekts nicht speichern möchten, sodass das Muster möglicherweise nicht für Sie "out-of-the-box" funktioniert.

Sie könnten es jedoch möglicherweise so ändern, dass das Memento nur die Deltas und nicht den vollständigen Objektstatus enthält. Dies würde zu einem geringeren Speicherbedarf führen. Es würde aber auch bedeuten, dass Sie die Zustände nur in der Reihenfolge zurücksetzen können, da ihre Deltas aufeinander aufbauen würden.

1

Sie können die Befehls- und Memento-Muster koppeln. Sie können konkrete Befehle haben, die die Commit- und Rollback-Operationen und Mementos implementieren, die den Status des geänderten Objekts vor dem Festschreiben speichern.