2010-12-15 5 views
9

最近、Greg Youngのビデオをいくつか見てきましたが、なぜSetters on Domainオブジェクトに対して否定的な態度があるのか​​理解しようとしています。私はDomainオブジェクトがDDDのロジックで「重い」と考えられていたと思いました。悪い例の良い例がオンラインにありますか?それを修正する方法はありますか?例や説明は良いです。これは、CQRS方式で保存されたイベントにのみ適用されますか、これはDDDのすべてに適用されますか?ドメインドリブンデザイン、ドメインオブジェクト、セッターに関する態勢

答えて

11

私は他の理由で不変物についてのRoger Alsingの答えを補うためにこの答えを寄稿しています。

セマンティック情報

ロジャーは明らかに、プロパティのセッターが意味情報を運ばないと説明しました。 Post.PublishDateのようなプロパティの設定を許可すると、投稿が公開されたかどうか、または公開日が設定されているかどうかわからないため、混乱が生じる可能性があります。私たちは、これが記事を公開するために必要なすべてであることを確かめることはできません。オブジェクト "インタフェース"は "意図"をはっきりと示さない。

「有効」のようなプロパティは「取得」と「設定」の両方に十分な意味を持ちます。これはすぐに有効なものであり、SetActive()またはActivate()/ Deactivate()メソッドの必要性は、プロパティセッターでセマンティクスが欠落しているという理由だけではわかりません。

不変

ロジャーはまた、プロパティのセッターを通じて不変量を壊すの可能性について話しました。これは絶対に正しいことであり、読み込み専用のプロパティとして「Rogerの例を使用するための三角形の角度」として「結合不変値」を提供し、これらをまとめて設定するメソッドを作成する必要があります。すべての組み合わせを1つのステップで検証します。

プロパティの順序の初期化/依存関係を設定

それは一緒に変更/検証されるべきである特性を有する問題を引き起こすので、これは不変での問題と類似しています。次のコードを想像してください:

public class Period 
    { 
     DateTime start; 
     public DateTime Start 
     { 
      get { return start; } 
      set 
      { 
       if (value > end && end != default(DateTime)) 
        throw new Exception("Start can't be later than end!"); 
       start = value; 
      } 
     } 

     DateTime end; 
     public DateTime End 
     { 
      get { return end; } 
      set 
      { 
       if (value < start && start != default(DateTime)) 
        throw new Exception("End can't be earlier than start!"); 
       end = value; 
      } 
     } 
    } 

これはアクセス順序の依存関係を引き起こす「設定」検証の素朴な例です。次のコードは、この問題を示しています

 public void CanChangeStartAndEndInAnyOrder() 
     { 
      Period period = new Period(DateTime.Now, DateTime.Now); 
      period.Start = DateTime.Now.AddDays(1); //--> will throw exception here 
      period.End = DateTime.Now.AddDays(2); 
      // the following may throw an exception depending on the order the C# compiler 
      // assigns the properties. 
      period = new Period() 
      { 
       Start = DateTime.Now.AddDays(1), 
       End = DateTime.Now.AddDays(2), 
      }; 
      // The order is not guaranteed by C#, so either way may throw an exception 
      period = new Period() 
      { 
       End = DateTime.Now.AddDays(2), 
       Start = DateTime.Now.AddDays(1), 
      }; 
     } 

それは(日時をデフォルトに設定両方の日付と「空」の期間、)でない限り、我々は期間オブジェクト(上の終了日を過ぎて開始日を変更することはできませんので、 - はい、それは素晴らしいデザインではありませんが、私が意味することを得る...)開始日を最初に設定しようとすると例外がスローされます。

オブジェクトイニシャライザを使用すると、より深刻になります。 C#ではどのような順序付けも保証されていないため、安全な前提はできません。また、コンパイラの選択に応じて、コードが例外をスローする場合もあります。悪い!

これは最終的にはクラスの設計上の問題です。プロパティは両方の値を更新していることを「知る」ことができないため、両方の値が実際に変更されるまで検証を「オフ」にすることはできません。両方のプロパティを読み取り専用にして、同時に(オブジェクトイニシャライザの機能を失う)、またはプロパティから検証コードをすべて削除する(IsValidのような別の読み取り専用プロパティを導入するか、いつでも必要に応じて)。

ORM "水和" *

水和は、単純なビューでは、オブジェクトに戻って永続データを取得を意味します。私にとってこれは実際にプロパティ設定者の背後にロジックを追加することで最大の問題です。

多くの/ほとんどのORMは、永続化された値をプロパティにマップします。プロパティー・セッター内でオブジェクト状態(他のメンバー)を変更する検証ロジックまたはロジックがあると、「不完全な」オブジェクト(まだ読み込まれていないオブジェクト)に対して検証しようとします。これは、フィールドの「水和」の順序を制御することができないため、オブジェクトの初期化の問題と非常によく似ています。

ほとんどのORMでは、プロパティではなくプライベートフィールドにマップすることができます。これによりオブジェクトが水和されますが、検証ロジックが大部分のプロパティセッター内にある場合は、読み込まれたオブジェクトが有効かどうかを判定します。

多くのORMツールは、フィールドにマッピングする仮想プロパティ(またはメソッド)を使用して遅延ロード(ORM!の基本的な側面)をサポートするため、ORMがフィールドにマップされた遅延ロードオブジェクトを不可能にします。最後に

結論だから、

、オームズはとして最高の彼らは、フィールドが設定されている順に応じて、驚くべき例外を防ぐことができるよう実行することができ、コードの重複を避けるために、ロジックを移動するのが賢明ですプロパティセッターから離れて。

私はまだ、この '検証'ロジックがどこにあるべきかを考えています。オブジェクトの不変量の側面はどこで検証されますか?より高いレベルのバリデーションはどこに置くのですか? ORMのフックを使用して検証を実行しますか(OnSave、OnDelete、...)? Etc.など。しかし、これはこの答えの範囲ではありません。

+1

あなたの例では、setterを避けるために 'period.setDateBoundries(startDate、endDate)'のような単一の関数を使うべきですか?ダミーセッターの代わりにビジネス上の意味を持つ単一の関数? – Songo

+1

@ソンゴそれは私がやることです。しかし、1つだけを変更するために両方の日付を渡す必要があると不満を表明することもできます(期間を延長したい場合は、両方の日付を渡す必要があると想像してください)...しかし、ちょっと、 ?)これを考えてみましょう:別のものに依存するプロパティで終わるたびに、それらを変更するメソッドや、より意味のある値を追加するメソッドを導入することをお勧めします(Period.Extendなど... )! – Loudenvier

+0

私は配列を渡すことを検討しています。 IDEのヒントではあまり役に立ちませんが、どのような順序でもデータを渡すことができます。状況に応じてデータを追加または省略することもできます(つまり、完全な日付スパン、または1つの日付のみ)。とにかく検証が行われているので、パラメータが与えられているか省略されているかどうかを確認することは、はるかに負担に見えません。 – prograhammer

1

セッターは単に値を設定します。 "heavy" with logicではありません。

わかりやすい名前のオブジェクトのメソッドは、"heavy" with logicであり、ドメイン自体にアナログがあります。

7

この理由は、エンティティ自体が状態を変更する責任を負う必要があるためです。エンティティ自体の外のどこにでもプロパティを設定する必要がある理由はありません。

単純な例は、名前を持つエンティティです。パブリックセッターがいる場合は、アプリケーションのどこからでもエンティティの名前を変更できます。代わりにそのセッターを削除して、ChangeName(string name)のようなメソッドをエンティティに置くと、名前を変更する唯一の方法になります。そうすれば、名前を変更するときに常に実行されるあらゆる種類のロジックを追加することができます。これは、名前を変更する方法が1つしかないためです。これはまた、名前を何かに設定するだけで、はるかに明確になります。

これは基本的に、状態を非公開にしている間にエンティティの動作を公開することを意味します。

+0

もう一度セッターになります。しかし、この場合、プロパティの代わりにメソッド(またはメッセージコンシューマ)があり、ドメインオブジェクトをアクティブにする(DTOだけでなく)。そして、記事http://martinfowler.com/bliki/AnemicDomainModel.html –

+0

@Vsevolod Parfenovで説明されている必要がある(または必要でない)理由。今これは非常に簡単な例でした。しかし時には、ある種のロジックをそのメソッドに追加する必要があるかもしれません。他のフィールドを更新したい場合もあります。 –

+0

プロパティのSETTERは、メソッドの単なる構文上の砂糖です。プロパティにロジックがある場合、ドメインオブジェクトはDTOだけでなくアクティブオブジェクトになります。実際にはメソッドを追加したばかりですが、プロパティセッターとして「隠されています」です。エンティティ名を変更する場所は1つだけです(Nameプロパティのプロパティ設定ツール)。したがって、これらは不動産設定者に対する否定的な態度の理由ではありません。他の理由があります(私は答えで詳しく述べます):不変量、シーケンス依存性、ORMツールからの実体を水和する際の問題 – Loudenvier

2

元の質問には.netというタグが付けられていますので、エンティティを直接ビューにバインドしたいコンテキストのための実用的なアプローチを提出します。

これは悪い習慣であり、ビューモデル(MVVMなど)があるはずですが、いくつかの小さなアプリケーションでは、IMHOを過度にパターン化しないほうが理にかなっています。

プロパティを使用することは、すぐに使用できるデータバインディングが.netで機能する方法です。おそらく、データバインディングが両方向で動作する必要があるため、INotifyPropertyChangedを実装し、Setterロジックの一部としてPropertyChangedを発生させることが文脈上意味をなさないかもしれません。エンティティは、例えば、次のようにすることができる。クライアントが無効な値を設定し、そのコレクションがUIに表示された場合、壊れたルールコレクションなどに項目を追加します(CSLAはその概念を持っていましたが、おそらくまだそうです)。作業単位は、あまりにも遠くに来なければ、無効なオブジェクトを永続させることを後に拒否します。

私は、デカップリング、不変性などを大幅に正当化しようとしています。私はいくつかの文脈では、よりシンプルなアーキテクチャが求められているということです。

+0

+1私は、あなたが挙げた正確な理由のためにコンセプトに戸惑っています。 Silverlight MVVMといくつかのCSLAの古い経験に精通しています。他の.NET開発者からのコメントが大好きです。 – BuddyJoe

10

セッターはいかなる意味情報も運びません。

blogpost.PublishDate = DateTime.Now; 

投稿が公開されていることを意味しますか? 公開日が設定されているだけですか?

は考えてみましょう:

blogpost.Publish(); 

これは明らかにブログ投稿が公開されなければならないという意思を示しています。

また、セッターはオブジェクト不変量を破壊することがあります。 たとえば、 "Triangle"エンティティがある場合、不変量はすべての角度の合計が180度になるようにする必要があります。我々はセッターを持っている場合

Assert.AreEqual (t.A + t.B + t.C ,180); 

今、我々は簡単に不変条件を破ることができます。

t.A = 0; 
t.B = 0; 
t.C = 0; 

だから我々はすべての角の和が0で三角形を持っている... はそれが本当にトライアングルですか?私はいいえと言うだろう。メソッドとセッターの交換

は不変量を維持するために私たちを強制することができます:

t.SetAngles(0,0,0); //should fail 

このような呼び出しは、これはあなたのエンティティのための無効な状態を引き起こすことを告げる例外をスローする必要があります。

したがってセッターの代わりにメソッドを使ってセマンティクスと不変量を取得します。

+0

私はsettersを使用してinvariantを壊していない限り、OKですか?つまり、ユーザーが顧客情報(名、ミドルネーム、姓)を編集していて唯一の不変条件が空でもNULLでもない場合は、各フィールドのセッターを使用しても問題ありませんか? – Songo

-1

私はここにいるかもしれませんが、セッターメソッドとは対照的に、セッターを使用して設定する必要があります。私はこれにはいくつかの理由があります。

a).Netで意味があります。すべての開発者はプロパティについて知っています。それはあなたが物の上にものを置く方法です。なぜドメインオブジェクトのそれから逸脱するのですか?

b)セッターはコードを持つことができます。 3.5の前に、私は信じて、オブジェクトを設定するには、セッターで検証を置くことは非常に簡単で、芋エレガントで内部変数とプロパティの署名

private object _someProperty = null; 
public object SomeProperty{ 
    get { return _someProperty; } 
    set { _someProperty = value; } 
} 

から成っていました。イリノイ州では、ゲッターとセッターはいずれにしてもメソッドに変換されます。なぜコードを複製するのですか?

上記のPublish()メソッドの例では、私は完全に同意しています。他の開発者がプロ​​パティを設定したくない場合があります。それはメソッドで処理する必要があります。しかし、.Netがプロパティ宣言で必要とするすべての機能を既に提供している場合、すべてのプロパティに対してsetterメソッドを使用することは意味がありますか?

Personオブジェクトがある場合、理由がない場合、そのオブジェクトのすべてのプロパティのメソッドを作成するのはなぜですか?

関連する問題