2009-04-14 7 views
4

Ich habe eine Order-Klasse, die eine Reihe von definierten Zuständen durchläuft. Um dies zu unterstützen, habe ich das State-Muster so implementiert, dass das Order-Objekt über ein CurrentState-Member verfügt, das eine IOrderState-Schnittstelle implementiert. Ich habe dann konkrete Implementierungen dieser Schnittstelle wie OrderStateNew, OrderStateDelivered etc usw.Wie sollte ein Objekt, das das Zustandsmuster verwendet, in den nächsten Zustand übergehen?

Meine Frage ist, was ist der richtige Weg, das Order-Objekt zwischen Zuständen zu überführen? Ist es akzeptabel, eine Order.SetState() -Methode zu verwenden, die es einem externen Dienst ermöglicht, den Status festzulegen? Die Kriterien, die die Statusänderungen bestimmen, werden extern auf dem Order-Objekt gespeichert, so dass dies die offensichtliche Antwort scheint, aber ich bin etwas unruhig darüber, eine öffentliche Methode auf meinem Objekt zu haben, um etwas so grundlegendes zu ändern.

Zusätzliche Klärung Ich dachte, es nützlich sein könnte, etwas ausführlicher über meine Implementierung hinzufügen, weil ich frage mich, ob ich wirklich bin das Muster richtig in erster Linie verwendet wird. Hier ist die pulbic API für die Erstellung und Genehmigung von einer Bestellung

Dim orderFacade As New OrderFacade 
Dim order = orderFacade.createFrom(customer) 

' Add lines etc 

' This will validate the order and transition it to status 'Authorised' 
Dim valid = orderFacade.Authorise(order) 

' This will commit the order, but only if it is at status 'Authorised' 
Dim result = orderFacade.Commit() 

Die OrderFacade.Authorise() Funktion so etwas wie dieses

Public Function Authorise(ByRef originalOrder As Order) As ValidationSummary 

    If originalOrder.CurrentState.CanAuthorise() Then 

     Dim validator = OrderValidatorFactory.createFrom(originalOrder) 
     Dim valid = validator.ValidateOrder(originalOrder) 

     If valid.IsValid Then 
      originalOrder.SetOrderStatus(OrderStatus.Authorised) 
     End If 

    Return valid 

    End If 

End Function 

sieht Wie Sie sehen können, ist das Aktuellerstatus Mitglied der aktuelle IOrderState Implementierung, die bestimmt, Welche Aktivitäten sind für das Objekt gültig? Ich frage mich, ob dies für den Übergang und nicht für die OrderFacade verantwortlich sein sollte?

+0

bezogen: http://stackoverflow.com/questions/1647631/c-state-machine-design – jldupont

Antwort

3

Sie können den Status implizit und nicht durch Zuweisung ändern.

In fast allen Fällen, die ich je gesehen habe, kann der Staat von anderen Eigenschaften abgeleitet werden (hoffentlich innerhalb der Klasse). Wenn ja, behalte den Zustand nicht bei, sondern leite ihn bei Bedarf ab. Andernfalls enden Sie oft mit problematischen Unstimmigkeiten zwischen abgeleiteten und zugewiesenen Werten. (Und in meiner Erfahrung „abgeleitet“ ist immer rechts.)

(Die Komplexität oft ist das Transaktionsprotokoll für die Klasse zu überprüfen, und betrachtet das jüngste Ereignis (n). Aber es lohnt sich trotzdem.)

1

Sie können die Sichtbarkeit der Methode beispielsweise auf "privat" reduzieren. Aber in Ihrem Fall denke ich, dass dies die einzige Möglichkeit ist, eine abstrakte Elternklasse zu haben, die die Zustandsmaschine implementiert und nur eine Gruppe von nextState (inputParameter) -Methoden hat, die den Status der Reihenfolge in den entsprechenden Zustand verschieben.

1

Ich glaube nicht, dass es eine "richtige" Antwort dafür gibt; Es hängt wirklich von der Architektur ab, die Sie für Ihre Zustandsmaschine gewählt haben. Wenn die gesamte Logik zum Ändern des Zustands in Ihrer Order-Klasse gekapselt wäre, würde ich sagen, dass es eine schlechte Übung wäre, eine SetState-Methode verfügbar zu machen. Da Sie jedoch einige der zustandsbestimmenden Logikelemente bereits außerhalb der Order-Klasse platziert haben, erscheint es sinnvoll (und notwendig), eine öffentliche SetState-Methode oder etwas Ähnliches verfügbar zu machen.

Natürlich wäre Ihre andere Wahl, die zustandsbestimmende Logik in die Order-Klasse zu verschieben, obwohl es auf der Grundlage Ihrer Angaben scheint, dass dies eine sehr komplexe Klasse erzeugen würde.

Wie auch immer, kurz gesagt, Muster sind wirklich nur da, um Ihnen zu helfen, Ihren Code zu entwerfen, nicht um feste und schnelle Regeln festzulegen. Sie sollten Muster auf die Architektur anwenden, die am besten funktioniert, und nicht nach Mustern.

2

Eine SetState() - Methode hätte Vorteile beim Erweitern von Aufträgen mit weiteren Zuständen sowie beim Ändern von Instrumenten - aber ich würde es nicht empfehlen. In der State pattern geht es darum, Verhaltensweisen zu erfassen, die für unterschiedliche Zustände in separaten Klassen spezifisch sind, und nicht darum, wie man statusbehaftete Schnittstellen zu anderen Klassen darstellen kann.

Für Bestellungen, denken Sie an die Geschäftsereignisse, die natürlich kommen (z. B. Bestätigung, Bestätigung, Versandbenachrichtigung, Versand, Rechnung & c) und entwerfen Sie eine explizite Schnittstelle um sie herum. Wie Sie die Schnittstelle gestalten, hängt davon ab, wie Ihre Anwendungslogik strukturiert ist und wie sie von anderen Schichten verwendet wird. Die klassische Antwort besteht darin, abstrakte Methoden für jedes Geschäftsereignis zu definieren (z. B. Confirm(), Acknowledge(), ShipDateChanged()). Wenn Sie z.B.C# Sie könnten sich entscheiden, mit eingehenden und ausgehenden Ereignissen von Ihren Auftragsobjekten zu gehen. Oder Sie könnten versuchen, eine Mischung oder eine Kombination davon zu versuchen. Der Hauptpunkt ist, dass eine SetOrderState() - Schnittstelle nicht sehr beschreibend ist und wahrscheinlich zu einer plumpen Implementierung führt (große Methoden in jeder Ihrer OrderState-Klassen).

Dann wiederum, eine SetState() - Methode innerhalb Ihrer Klassen (aufgerufen von jeder Ihrer spezifischen Methoden oder Ereignisse) könnte OK sein, wenn Sie nicht viel Code um die verschiedenen Statusänderungen haben: aber ich würde nicht t das als eine externe Schnittstelle aussetzen. Der Nachteil besteht darin, dass es zu Überschneidungen zwischen den Methoden Ihrer internen IOrderState-Schnittstelle und der extern offen gelegten Bestellschnittstelle kommen kann.

Dies ist ein Urteilsspruch, aber wenn ich Sie wäre, würde ich mit Ihrem Instinkt gehen, um die Details Ihrer staatlichen Implementierung an Kunden zu offenbaren. Code, der Ihre Order-Klasse verwendet, sollte lesbar und verständlich sein.

+0

Vielen Dank für Ihren Kommentar. Ich habe eine weitere Erläuterung meiner Architektur hinzugefügt, hoffentlich sollte das helfen. Ich frage mich, ob ich es tatsächlich schwieriger für mich mache, wenn ich versuche, meine Order-Klasse so leicht wie möglich zu machen. – James

1

Ich denke, der Übergang zwischen den Zuständen sollte in der Klasse sein.

Wenn Sie zum Beispiel eine bestimmte Aktion in der Reihenfolge ausführen, weiß die Bestellung, wie sie in einen anderen Status wechseln soll. Beispiel:

Eine andere Alternative könnte darin bestehen, eine neue Klasse für Transformer zu erstellen, die eine Methode wie diese implementiert.

public class Transformer 
{ 
    public static void setState(Order o, IState s) 
    { 
     //Change the State. 
    } 
} 

Oder yoy können eine ENUM nutzen die Staaten dort zu setzen:

public class Transformer 
{ 
    public static void setState(Order o, StatesEnum s) 
    { 
     //Change the State. 
    } 
} 

Aber ich empfehle die Klasse ihren eigenen Zustand zu manipulieren. Aber erinnere dich an die Komplexität, die du auf der anderen Seite haben wirst.

Mit freundlichen Grüßen!

+0

Danke für Ihre Kommentare. Ich habe versucht, meine Order-Klasse so einfach wie möglich zu machen und stattdessen meine Service-Ebene manipulieren und bewerten von außen (was ich denke, ist ähnlich zu Ihrem Transformer-Vorschlag). Was sehen Sie, wenn Sie dies innerhalb der Ordensklasse tun? – James

+0

Hallo James; 1) Sie delegieren alle Kenntnisse des Zustandsübergangs an die Klasse, die gut ist. 2) Sie lassen andere den Zustand nicht manipulieren (manchmal fehleranfällig) 3) Sie können den Zustandsübergangsfluss leicht steuern: Sie sind nicht auf Bewertungen Dritter angewiesen. Die Klasse weiß, wie man sich ändert :) – MRFerocius