2012-04-04 6 views
5

単純な列挙を使用して、エンティティの状態を表すことがよくあります。問題は、主に状態に依存する動作、または状態遷移が特定のビジネスルールに従わなければならない動作を導入するときに発生します。ステートパターンとドメイン駆動デザイン

は(その状態を表すために列挙を使用しています)、次の例を見てみましょう:

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 
    } 
} 

あなたはこのクラスはすぐに我々は完全拒否のための方法を、追加として、非常に冗長になることを見ることができ、削除など

代わりに、私たちは私たちがオブジェクトとしてそれぞれの状態をカプセル化することを可能にする状態パターン、導入することができます:これはbetweeの移行が容易になり

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; 
    } 
} 

をn個の状態は、現在の状態に基づいてロジックを実行するか、我々がする必要がある場合は、新しい状態を追加します。このカプセル化は、クリーナーようだが十分な状態を与え

// transition state 
vacancy.State.Approve(); 

// conditional 
model.ShowRejectButton = vacancy.State.CanReject(); 

、これらはあまりにも非常に冗長になることができます。私はGreg Young's post on State Pattern Misuseを読んでいます。代わりにポリモルフィズムを使用しています(私はApprovedVacancy、UnapprovedVacancyなどのクラスを持っています)。しかし、これがどう役立つか分かりません。

このような状態遷移をドメインサービスに委任するか、この状態でStateパターンを使用するのは正しいですか?

答えて

5

質問に答えるには、これをドメインサービスに委任するべきではなく、州のパターンの使用がほぼ正しいとします。

具体的には、オブジェクトの状態を維持する責任はそのオブジェクトに属しているため、これをドメインサービスに委ねると、貧血モデルになります。これは、州の変更の責任を他のパターンの使用によって委任することはできないと言っているわけではありませんが、これはオブジェクトの消費者にとって透明でなければなりません。

これは、私にあなたの状態パターンの使用につながります。ほとんどの場合、パターンを正しく使用しています。あなたが少し逸脱している部分は、あなたの法則違反です。あなたのオブジェクトの消費者はあなたのオブジェクトに到達してその状態のメソッドを呼び出すべきではありません(例えばvacancy.State.CanReject())が、このオブジェクトをStateオブジェクトに委譲する必要があります(vacancy.CanReject() > bool CanReject(){return _state.CanReject();})。あなたのオブジェクトの消費者は、あなたが州のパターンを使用していることを知る必要はありません。

あなたが参照した記事にコメントするために、Stateパターンはメカニズムを促進するので多態性に依存します。 State実装をカプセル化するオブジェクトは、何もしない、例外をスローする、何らかのアクションを行うものであれ、現在割り当てられている実装に呼び出しを委譲できます。また、Stateパターン(またはその他のパタ​​ーン)を使用してLiskov Substitution Principle違反を引き起こすことは確かに可能ですが、オブジェクトが例外をスローするかどうかによって判断されるのではなく、オブジェクトの変更既存のコードを考慮して作成することができます(詳細は、thisを参照してください)。

+0

消費者が状態を直接変更してはならない場合、これは私のエンティティの各状態遷移のためのメソッドで終わることを意味します。では、私はここで本当に何を得ているのですか? –

+2

あなたはカプセル化され、if文の束が存在しない:)公式の利点は次のとおりです。1.状態別の振る舞いと異なる状態の振る舞いをローカライズします。 2.状態遷移を明示します。 3.状態オブジェクトを共有することができます。 –

+0

ありがとうございます。最終的な質問の1つとして、Vacancyをドキュメントストア(RavenDB)に保存しています。関連する状態オブジェクトをロードするか、状態オブジェクト全体を格納するために使用される列挙型を永続化することを提案してください –

関連する問題