Wir verwenden oft einfache Enumerationen, um einen Zustand auf unseren Entitäten darzustellen. Das Problem tritt auf, wenn wir Verhaltensweisen einführen, die weitgehend vom Staat abhängen oder bei denen Zustandsübergänge bestimmte Geschäftsregeln einhalten müssen.State Pattern und Domain Driven Design
Nehmen Sie das folgende Beispiel (das eine Aufzählung verwendet Zustand darzustellen):
public class Vacancy {
private VacancyState currentState;
public void Approve() {
if (CanBeApproved()) {
currentState.Approve();
}
}
public bool CanBeApproved() {
return currentState == VacancyState.Unapproved
|| currentState == VacancyState.Removed
}
private enum VacancyState {
Unapproved,
Approved,
Rejected,
Completed,
Removed
}
}
Sie können sehen, dass diese Klasse bald recht ausführliche werden wird, wie wir Methoden hinzufügen für Abweisen, Complete, entfernen usw.
Stattdessen können wir den Staat Muster einführen, die uns jeden Zustand als Objekt zu kapseln erlaubt:
public abstract class VacancyState {
protected Vacancy vacancy;
public VacancyState(Vacancy vacancy) {
this.vacancy = vacancy;
}
public abstract void Approve();
// public abstract void Unapprove();
// public abstract void Reject();
// etc.
public virtual bool CanApprove() {
return false;
}
}
public abstract class UnapprovedState : VacancyState {
public UnapprovedState(vacancy) : base(vacancy) { }
public override void Approve() {
vacancy.State = new ApprovedState(vacancy);
}
public override bool CanApprove() {
return true;
}
}
Dies macht es einfach zwische den Übergang n Staaten, führen Logik basierend auf dem aktuellen Stand oder neue Staaten hinzu, wenn wir brauchen, um:
// transition state
vacancy.State.Approve();
// conditional
model.ShowRejectButton = vacancy.State.CanReject();
Diese Kapselung scheint saubere, aber genug, um Zustände gegeben, auch diese sehr ausführlich werden können. Ich lese Greg Young's post on State Pattern Misuse, was vorschlägt, stattdessen Polymorphismus zu verwenden (also hätte ich ApprovedVacancy, UnapprovedVacancy usw. Klassen), aber kann nicht sehen, wie mir das helfen wird.
Sollte ich solche Zustandsübergänge an einen Domain-Service delegieren oder ist meine Verwendung des State-Pattern in dieser Situation korrekt?
Wenn der Verbraucher den Status nicht direkt ändern sollte, bedeutet dies, dass ich mit einer Methode für jeden Statusübergang auf meiner Entität enden werde. Was kann ich hier wirklich gewinnen? –
Sie erhalten Kapselung und das Fehlen einer Reihe von if-Anweisungen :) Hier ist die offizielle Vorteile: 1. Es lokalisiert das zustandsspezifische Verhalten und Partitionen Verhalten für verschiedene Staaten. 2. Es macht Zustandsübergänge explizit. 3. Zustandsobjekte können geteilt werden. –
Danke.Eine abschließende Frage - wir bestehen auf Stellenausschreibungen in einen Dokumentenspeicher (RavenDB). Würden Sie vorschlagen, eine Enumeration beizubehalten, die zum Laden des relevanten Zustandsobjekts verwendet wird oder nur das gesamte Zustandsobjekt speichert? –