web-dev-qa-db-ja.com

クラス不変条件がそのように規定している場合でも、値オブジェクトのコンストラクターは機能しない必要がありますか?

今日は同僚と話し合った。

クラスには、クラスの外部から対話したときにオブジェクトが有効な状態になるようにする責任があることを理解しています。このルールの理由は、クラスがそのユーザーが誰であるかを知らず、違法な方法で対話された場合に予想どおりに失敗するはずであるためです。私の意見では、そのルールは、プリミティブ値を保持するだけの、不変でややプリミティブなオブジェクトを含むすべてのクラスに適用されます。

今日話し合った特定の状況では、そのようなValueObjectのコンストラクターは、有効な状態に必要な値を取得するために、いくつかの作業を行う必要がありました(いくつかのゲッターを呼び出す必要がありました)。計算結果間の相関は、ValueObjectのクラス不変条件の条件です。つまり、それらは同じソースを持っている必要があります。

これはクラスの図です(phpコード例):

class Example {

    private $someValue;
    private $computedResultA;
    private $computedResultB;

    public function __construct($someValue, $someDependency) {
        $this->someValue = $someValue;
        $this->computedResultA = $someDependency->computeResultA();
        $this->computedResultB = $someDependency->computeResultB();
    }
}

私の同僚は、ValueObjectのインスタンスを作成するクラスで結果を計算し、これらのプリミティブな結果をそのValueObjectのコンストラクターに渡す方がよいと主張しています。結局のところ、コンストラクターは機能してはならない、と彼は考えています。さらに、ValueObjectのコンストラクターにモックを提供する必要がある場合、テストコードは大きくなります。これは、単にプリミティブ値が提供される場合は必要ありません。そして明らかに、コードのテストはテストされたコードよりも重要です。

これは彼が好むコードの図解です:

class Example {

    private $someValue;
    private $computedResultA;
    private $computedResultB;

    public function __construct($someValue, $computedResultA, $computedResultB) {
        $this->someValue = $someValue;
        $this->computedResultA = $computedResultA;
        $this->computedResultB = $computedResultB;
    }
}

コンストラクターが機能してはならない理由を誰かが説明できますか?グーグルはコンストラクターが副作用を引き起こしてはならないと私に言います。しかし、ある種の計算結果を明示的に具体化する(値)オブジェクトについてはどうでしょうか?その計算結果は常に代わりにファクトリで処理され、その後、値オブジェクトに渡される必要がありますか?それはオブジェクトの整合性制約を維持することに失敗しませんか?

2
user2180613

コンストラクターが機能してはならない理由を誰かが説明できますか?

コンストラクターには、エラーケースを処理するためのメカニズムが制限されているためです。そのタイプのオブジェクトを返すか、例外をスローする必要があります。別のタイプを返すことはできません。失敗を知らせることはできません。 nullを返すことはできません。あなたは(ほとんどの場合)例外をスローするだけで、発信者にそれを処理させます。

しかし、ある種の計算結果を明示的に具体化する(値)オブジェクトについてはどうでしょうか?

それらの計算が適度に高速で、例外的なシナリオでのみ失敗する場合、それは許されます。これらの計算を暗黙的に実行すると、単純化されたインターフェイスを介してこれらのハード不変条件を適用することにより、コードがよりクリーンになることがあります。

しかし、一般的なガイドラインとして、コンストラクターは機能しないはずです。

その計算結果は常に代わりにファクトリで処理され、その後、値オブジェクトに渡される必要がありますか?それはオブジェクトの整合性制約を維持することに失敗しませんか?

常にはおそらく強すぎますが、そうです、一般的にはそうすべきです。一部の言語では、静的関数を提供して、プライベートコンストラクターを使用するオブジェクトを作成できます。これにより、オブジェクトの不変性が維持されると同時に、エラー状態をより適切に処理できるようになります。

2
Telastyn