単純な列挙を使用して、エンティティの状態を表すことがよくあります。問題は、状態に大きく依存する動作を導入した場合、または状態遷移が特定のビジネス ルールに従う必要がある場合に発生します。
次の例を見てください (列挙を使用して状態を表します)。
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
}
}
Reject、Complete、Remove などのメソッドを追加すると、このクラスはすぐに非常に冗長になることがわかります。
代わりに、各状態をオブジェクトとしてカプセル化できる State パターンを導入できます。
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;
}
}
これにより、状態間の遷移、現在の状態に基づいたロジックの実行、または必要に応じて新しい状態の追加が簡単になります。
// transition state
vacancy.State.Approve();
// conditional
model.ShowRejectButton = vacancy.State.CanReject();
このカプセル化はきれいに見えますが、十分な状態が与えられると、これらも非常に冗長になる可能性があります。代わりにポリモーフィズムを使用することを提案しているState Pattern Misuse に関する Greg Young の投稿を読みました (そのため、ApprovedVacancy、UnapprovedVacancy などのクラスが必要です) が、これがどのように役立つかわかりません。
このような状態遷移をドメイン サービスに委任する必要がありますか?それとも、この状況での状態パターンの使用は正しいですか?