web-dev-qa-db-ja.com

DDDの値オブジェクト-なぜ不変なのですか?

DDDの値オブジェクトが不変である理由がわかりません。また、これがどのように簡単に行われるのかもわかりません。 (重要な場合は、C#とEntity Frameworkに焦点を当てています。)

たとえば、従来のアドレス値オブジェクトについて考えてみましょう。 「123MainSt」を「123MainStreet」に変更する必要がある場合、なぜmyCustomer.Address.AddressLine1 = "123 Main Street"と言う代わりに、まったく新しいオブジェクトを作成する必要がありますか? (Entity Frameworkが構造体をサポートしていても、これは問題になりますね?)

値オブジェクトにはIDがなく、ドメインオブジェクトの一部であるという考えは理解していますが、不変性が良いことである理由を誰かが説明できますか?


[〜#〜] edit [〜#〜]:ここでの最後の質問は、「不変性が良いことである理由を誰かが説明できるか値オブジェクトに適用される? "混乱させて申し訳ありません!


[〜#〜] edit [〜#〜]:わかりやすくするために、CLR値型(対参照型)については質問していません。値オブジェクトのより高いレベルのDDD概念について質問しています。

たとえば、Entity Frameworkの不変の値型を実装するためのハックっぽい方法は次のとおりです。 http://rogeralsing.com/2009/05/21/entity-framework-4-immutable-value-objects =。基本的に、彼はすべてのセッターをプライベートにします。なぜこれを行うのに苦労するのですか?

35
Hobbes

DDDとは関係のない、スレッドセーフなどに関するすべてのクレイジーな答えは無視してください。 (スレッドセーフなO/Rマッパーやその他のDDD対応のダルはまだ見ていません)

重みの値オブジェクトを想像してみてください。 KG値オブジェクトがあるとしましょう。

サンプル(わかりやすくするために編集):

var kg75 = new Weight(75);
joe.Weight = kg75;
jimmy.Weight = kg75;

これを行うとどうなるでしょうか。

jimmy.Weight.Value = 82;

同じオブジェクト参照をまだ使用している場合は、joeの重みも変更されます。ジョーとジミーの両方に75kgを表すオブジェクトを割り当てたことに注意してください。ジミーが体重を増やしたとき、変化したのはkg75オブジェクトではなく、変化したのはジミーの体重です。したがって、82kgを表す新しいオブジェクトを作成する必要があります。

しかし、新しいセッションがあり、ジョーとジミーの両方をクリーンなUoWにロードするとどうなるでしょうか。

 var joe = context.People.Where(p => p.Name = "joe").First();
 var jimmy = context.People.Where(p => p.Name = "jimmy").First();
 jimmy.Weight.Value = 82;

それではどうなるでしょうか。ええと、あなたの場合のEF4は、アイデンティティなしでjoeとjimmyとそれらの重みをロードするので、2つの異なる重みオブジェクトを取得し、jimmysの重みを変更しても、joeの重みは以前と同じになります。

したがって、同じコードに対して2つの異なる動作があります。オブジェクト参照がまだ同じである場合、joeとjimmyの両方が新しい重みを取得します。 joeとjimmyがクリーンなuowにロードされている場合、変更の影響を受けるのはそのうちの1つだけです。

そして、それはかなり矛盾したimoになります。

不変のVOを使用することで、どちらの場合も同じ動作が得られ、オブジェクトグラフを作成するときに、オブジェクト参照を再利用してメモリフットプリントを小さくすることができます。

46
Roger Johansson

6が不変なのはなぜですか?

それを理解すれば、値オブジェクトが不変である理由を理解できます。

編集:私は今、この答えに私たちのダイアログを持ち上げます。

6は不変であるため、6のアイデンティティは、それが表すもの、つまり6つの何かを持っている状態によって決定されます。あなたはそれを変えることはできません6はそれを表します。さて、これが値オブジェクトの基本的な概念です。それらの値はそれらの状態によって決定されます。ただし、エンティティはその状態によって決定されません。 Customerは、姓または住所を変更しても、同じCustomerのままにすることができます。これが、値オブジェクトが不変でなければならない理由です。彼らの状態が彼らのアイデンティティを決定します。彼らの状態が変わると、彼らのアイデンティティも変わるはずです。

35
jason

私はパーティーにとても遅れていますが、私はこれについて自分で疑問に思っていました。 (コメントをいただければ幸いです。)

ここで明示的に引用されているとは思いませんが、エバンスの不変性への言及は、主に共有のコンテキストにあったと思います。

オブジェクトを安全に共有するには、オブジェクトは不変である必要があります。完全な置換以外では変更できません。 (Evans p100)

エヴァンの本には、「アドレスは値オブジェクトですか?誰が尋ねているのですか?」というサイドバーもあります。

ルームメイトがそれぞれ電気サービスを注文するように電話をかけた場合(つまり、2人の顧客が同じ住所を持っていた場合)、会社はそれを実現する必要があります。 [だから]アドレスはエンティティです。 (Evans p98)

あなたが与えた例では、顧客の自宅と会社の住所が両方とも123 MainStreetであったと仮定します。説明した修正を行うと、両方のアドレスが変更されますか?もしそうなら、そして私がエバンスを正しく読んでいるなら、あなたは本当にエンティティを持っているように聞こえます。

別の例をとると、顧客のフルネームを表すオブジェクトがあるとします。

public class FullName
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Customer
{
    public FullName Name { get; set; }
}

値オブジェクトがないと、以下は失敗します。

[Test]
public void SomeTest() {
    var fullname = new FullName { FirstName = "Alice", LastName = "Jones" };
    var customer1 = new Customer { Name = fullname };
    var customer2 = new Customer { Name = fullname };

    // Customer 1 gets married.
    customer1.Name.LastName = "Smith";

    // Presumably Customer 2 shouldn't get their name changed.
    // However the following will fail.
    Assert.AreEqual("Jones", customer2.Name.LastName);
}

一般的な利点の観点から、いくつかは DDDでは、値オブジェクトの実際の利点は何ですか? で取り上げられます。特に、作成時にVOを検証するだけで済みます。そうすれば、それが常に有効であることがわかります。

10
David

これは完全な答えではないかもしれません。私は不変性の利点についてのあなたの質問に答えているだけです。

  1. 不変オブジェクトはスレッドセーフだからです。状態を変更できないため、スレッドの干渉によって破損したり、一貫性のない状態で観察されたりすることはありません。
  2. 不変オブジェクトへの参照は、構築後に状態を変更できないため、コピーまたは複製しなくても簡単に共有またはキャッシュできます。
  3. 不変性のその他の利点については、こちらをご覧ください(LBushkinの回答) 文字列が不変である利点は何ですか?

これは例です 値オブジェクトが不変でなければならない理由についてのMartinFowlerによる。

了解しました。VOを不変にすることは必須ではありませんが(DDDの本でも不変である必要はないとは言われていません)、DDDでVOにする主なアイデアは、次のようなライフサイクルの複雑さに対処しないようです。エンティティのそれ。ここを見てください 詳細については

5

これはずっと前に尋ねられましたが、私は簡単で覚えやすいと思う例で答えを提供することにしました。その上、SOは多くの開発者のリファレンスとして機能し、この質問にぶつかった人は誰でもそれから利益を得ることができると思います。

属性によって定義されるため、値オブジェクトは不変として扱われます

値オブジェクトの良い例はお金です。ポケットの中の同じ5つの1ドル札を区別できないことは問題ではありません。あなたは通貨のアイデンティティを気にしません-その価値とそれが何を表すかだけを気にします。誰かがあなたの財布にあるものと5ドルの請求書を交換したとしても、あなたがまだ5ドルを持っているという事実は変わりません。

したがって、たとえば、C#では、お金を不変の値オブジェクトとして定義します。

public class Money
{
    protected readonly decimal Value;

    public Money(decimal value)
    {
        Value = value;
    }

    public Money Add(Money money)
    {
        return new Money(Value + money.Value);
    }

    // ...


    // Equality (operators, Equals etc) overrides (here or in a Value Object Base class). 
    // For example:

    public override bool Equals(object obj)
    {
        return Equals(obj as Money);
    }

    public bool Equals(Money money)
    {
        if (money == null) return false;
        return money.Value == Value;
    }
}
0
dpant

値オブジェクトは不変である必要があります。

多くの場合、不変オブジェクトは実際に生活を簡素化します。 ...そして、並行プログラミングをより安全でクリーンなものにすることができます 詳細

値オブジェクトが可変であると考えてみましょう。

class Name{
string firstName,middleName,lastName
....
setters/getters
}

元の名前がリチャードトーマスクックだったとしましょう

ここで、firstName(Martinに)とlastName(Bondに)のみを変更するとします。不変オブジェクトでない場合は、メソッドを使用して状態を1つずつ変更します。 Martin ThomasCookとして名前を付ける可能性は、Martin Thomas Bondは決して受け入れられません(また、後でコードを見る人に間違った考えを与え、さらなる設計で望ましくないドミノ効果をもたらします)。

可変値オブジェクトは、1つのトランザクションで与えられた変更に対して整合性制約を明示的に適用する必要があります。これは、不変オブジェクトで無料で与えられます。したがって、値オブジェクトを不変にすることは理にかなっています。

0
rhozet