Ein kleines Intro:
Die Klasse enthält Felder und Methoden (lassen Sie mich diesmal die Eigenschaften überspringen).
Felder repräsentieren einen Status der Klasse.
Methoden beschreiben Verhalten der Klasse.
In einer gut entworfenen Klasse wird eine Methode den Status der Klasse nicht ändern, wenn sie eine Ausnahme auslöst, richtig? (Mit anderen Worten, was auch immer passiert, der Zustand der Klasse sollte nicht verfälscht sein)
Frage:
Gibt es ein Framework, ein Designmuster, eine Best Practice oder eine Programmiersprache, um eine Abfolge von Methoden in einem transaktionalen Stil aufzurufen, so dass der Status einer Klasse nicht geändert wird (im Ausnahmefall) oder alles erfolgreich ist?
ZB:
%Vor% Sicherlich kann eine Ausnahme sowohl in MoveToState2()
als auch in MoveToFinalState()
auftreten. Aber von diesem Codeblock möchte ich, dass die Klasse foo
entweder im Zustand S1 oder S3 ist.
Dies ist ein einfaches Szenario mit einer einzigen Klasse beteiligt, keine if
s, keine while
s, keine Nebenwirkungen, aber ich hoffe, die Idee ist klar.
Sehen Sie sich das Memento-Muster
anDas Memento-Muster ist ein Softwaredesignmuster, das die Möglichkeit bietet, ein Objekt in seinen vorherigen Zustand zurückzuversetzen (Rückgängigmachen durch Rollback).
Das einfachste und verlässlichste "Muster", das hier verwendet wird, ist eine unveränderliche Datenstruktur.
Anstatt zu schreiben:
%Vor%Sie schreiben:
%Vor% Und implementieren Sie die Methoden entsprechend - das heißt, MoveToState2
ändert nichts an MyFoo
, es erzeugt ein neues MyFoo
, das sich in Zustand 2 befindet. Ähnlich wie beim Endzustand.
So funktionieren die String-Klassen in den meisten OO-Sprachen. Viele OO-Sprachen beginnen auch, unveränderbare Sammlungen zu implementieren (oder haben bereits implementiert). Sobald Sie die Bausteine haben, ist es ziemlich einfach, eine ganze unveränderliche "Entität" zu erstellen.
Nicht die effizienteste Methode, aber Sie könnten ein Objekt haben, das Ihre Transaktionsdaten darstellt. Wenn Sie eine Transaktion starten, erstellen Sie eine Kopie der Daten und führen Sie alle Vorgänge dafür aus. Wenn die Transaktion erfolgreich beendet wurde, verschieben Sie die Kopie in Ihre realen Daten - dies kann mithilfe von Zeigern erfolgen, muss also nicht zu ineffizient sein.
Funktionale Programmierung ist ein Paradigma, das gut zu Transaktionsberechnungen zu passen scheint. Da keine Nebenwirkungen ohne explizite Deklaration zulässig sind, haben Sie die volle Kontrolle über den gesamten Datenfluss.
Daher kann der Software-Transaktionsspeicher leicht funktional ausgedrückt werden - Siehe STM für F #
Die Schlüsselidee ist das Konzept Monaden . Eine Monade kann verwendet werden, um eine beliebige Berechnung durch zwei Primitive zu modellieren: Return um einen Wert zurückzugeben und Bind um zwei Berechnungen zu sequenzieren. Mit diesen beiden können Sie eine Transaktionsmonade modellieren, die alle Zustände in Form von Fortsetzungen steuert und speichert.
Man könnte versuchen, diese objektorientiert durch einen Staat + Memento Muster, aber im Allgemeinen sind Transaktionen in Imperativsprachen (wie die üblichen OO-Einsen) viel schwieriger zu implementieren, da Sie beliebige Nebeneffekte ausführen können. Aber Sie können sich natürlich ein Objekt vorstellen, das einen Transaktionsbereich definiert, der Daten speichert, validiert und wiederherstellt, wenn sie eine geeignete Schnittstelle dafür bereitstellen (die Muster, die ich oben erwähnt habe).
Dies wäre ziemlich hässlich, wenn es überall implementiert werden würde, aber nur den Zustand lokal zu speichern und dann im Falle einer Ausnahme wiederherzustellen, würde in einfachen Szenarien funktionieren. Sie müssen die Ausnahme abfangen und erneut auslösen, wodurch in einigen Sprachen möglicherweise der Kontext verloren geht. Es ist besser, wenn möglich, den Kontext zu umbrechen.
%Vor%.) Transaktionsspeicher passt hier am besten.
.) Eine Option könnte ein Transaktionsspeicher sein. Beispielimplementierung finden Sie hier: Ссылка
.) Memento-Muster
.) Lassen Sie mich auch ein mögliches Muster beschreiben, wie dieses Verhalten implementiert werden kann:
Definieren Sie eine Basisklasse TransactionalEntity
. Diese Klasse enthält ein Wörterbuch mit Eigenschaften.
Alle Ihre Transaktionsklassen erben von TransactionalEntity
und sollten über eine Art Abhängigkeitseigenschaften / Felder , dh Eigenschaften (Getter / Setter), die ihre Werte nicht in lokalen Klassenfeldern, sondern im Wörterbuch speichern, operieren. was in der Basisklasse gespeichert ist.
Dann definieren Sie TransactionContext
class. TransactionContext
class enthält intern eine Reihe von Wörterbüchern (ein Wörterbuch für jede Entität, die an der Transaktion teilnimmt). Wenn eine transaktionale Entität an der Transaktion teilnimmt, schreibt sie alle Daten in das Wörterbuch im Transaktionskontext. Dann brauchen Sie nur vier Methoden:
Zusammenfassend müssen Sie den Status in der Basisklasse TransactionalEntity
speichern und während der Transaktion TransactionalEntity
wird mit TransactionContext
zusammenarbeiten.
Ich hoffe, ich habe es gut genug erklärt.
Ich denke, ein Befehlsmuster könnte für dieses Problem gut geeignet sein. Linky.
Wenn Sie den Objektkopie-Ansatz verwenden, müssen Sie darauf achten, dass die zurückzustellenden Anweisungen nur die Objekte oder Daten selbst (und Aggregate) betreffen.
Aber die Dinge werden wirklich schwierig, wenn die Nebenwirkungen der Äußerungen "externer" sind. Zum Beispiel E / A-Operationen, Netzwerkanrufe. Sie müssen immer die allgemeinen Zustandsänderungen Ihrer Aussagen analysieren.
Es wird auch sehr schwierig, wenn Sie statische Daten (oder böse veränderbare Singletons) berühren. Das Wiederherstellen dieser isolierten Daten ist schwierig, weil andere Threads sie zwischenzeitlich geändert haben könnten (Sie könnten mit verlorenen Aktualisierungen konfrontiert werden).
Das Zurückkehren in die Vergangenheit ist oft nicht so trivial;)
Ich war erstaunt, dass niemand explizit das einfachste zu verwendende Muster vorgeschlagen hat ... das Zustandsmuster
Auf diese Weise können Sie auch die Methode 'finalState' eliminieren und einfach 'handle ()' verwenden. Woher weißt du, welcher Endzustand ist? Das Memento-Muster wird am besten mit dem Befehlsmuster verwendet und bezieht sich normalerweise auf GUI-Operationen zur Implementierung der Rückgängig / Wiederholen-Funktion.
Felder repräsentieren einen Zustand der Klasse
Felder repräsentieren den Status des instanzierten Objekts . Sie verwenden oft falsche Definitionen der OOP-Begriffe. Überprüfen und korrigieren.
Tags und Links architecture oop