2017-12-08 6 views
2

以前は、コンストラクタ内でオブジェクトの検証を実行し、検証が失敗したときに例外がスローされました。例えば:私のオブジェクトは、その一貫性を強化し、それが常に有効である保証する責任があるとして、私はこのアプローチを好む一方で通知パターンを使用したドメイン検証

class Name 
{ 
    const MIN_LENGTH = 1; 

    const MAX_LENGTH = 120; 

    private $value; 

    public function __construct(string $name) 
    { 
     if (!$this->isValidNameLength($name)) { 
      throw new InvalidArgumentException(
       sprintf('The name must be between %d and %d characters long', self::MIN_LENGTH, self::MAX_LENGTH) 
      ); 
     } 
     $this->value = $name; 
    } 

    public function changeName(string $name) 
    { 
     return new self($name); 
    } 

    private function isValidNameLength(string $name) 
    { 
     return strlen($name) >= self::MIN_LENGTH && strlen($name) <= self::MAX_LENGTH; 
    } 
} 

、私は例外の使用について過度に熱心行ったことがありません。上記のような例外の使用を主張する人がいますが、複数のオブジェクトに対して検証を実行するときに返すことができる検証メッセージの数は限られています。たとえば:

class Room 
{ 
    private $name; 

    private $description; 

    public function __construct(Name $name, Description $description) 
    { 
     $this->name = $name; 
     $this->description = $description; 
    } 
} 

class Name 
{ 
    public function __construct(string $name) 
    { 
     // do some validation 
    } 
} 

class Description 
{ 
    public function __construct(string $description) 
    { 
     // do some validation 
    } 
} 

NameDescriptionの両方が検証に失敗すると、私は両方のオブジェクトのための失敗メッセージ、最初に失敗した方のオブジェクトからだけではなく、単一の例外を返すことができるようにしたいです。

notification patternで少し読んだところ、私はこれが私のシナリオに適していると感じました。私が立ち往生するところでは、どのように検証を実行し、検証が失敗した場合にオブジェクトが無効な状態になるのを防ぐかです。

class Name 
{ 
    const MIN_LENGTH = 1; 

    const MAX_LENGTH = 120; 

    private $notification; 

    private $value; 

    public function __construct(string $name, Notification $notification) 
    { 
     $this->notification = $notification; 
     $this->setName($name); 
    } 

    private function setName(string $name) 
    { 
     if ($this->isValidNameLength($name)) { 
      $this->value = $name; 
     } 
    } 

    private function isValidNameLength(string $name) 
    { 
     if (strlen($name) < self::MIN_LENGTH || strlen($name) > self::MAX_LENGTH) { 
      $this->notification->addError('NAME_LENGTH_INVALID'); 
      return false; 
     } 
     return true; 
    } 

    public function hasError() 
    { 
     return $this->notification->hasError(); 
    } 

    public function getError() 
    { 
     return $this->notification->getError(); 
    } 
} 

私は、上記についていくつかの懸念があります。検証が失敗した場合、そのオブジェクトはまだ構築されるが、その$valueが有効な状態ではありませんnullある

  1. を。
  2. Nameを作成した後、hasErrorに電話して、検証エラーが発生したかどうかを確認する必要があります。
  3. 私はドメインオブジェクトをhasError/getErrorの機能でぼかしていますが、これはわかりやすい機能です。

このパズルの一部が欠落していますか?通知パターンを利用しても、オブジェクトが無効な状態にならないようにするにはどうすればよいでしょうか?

答えて

1

のようなもの?

Factoryパターン - あなたは、独自の不変を保つことができない値を作成することはありません - 別名「という名前のコンストラクタ」

あなたは、コンストラクタの検証がされているままにしておきます。

しかし、代わりにパブリックAPIからコンストラクタを取り出して、ファクトリでメソッドを呼び出すクライアントコードを配置します。その工場では、オブジェクトを構築するために、障害を管理する方法を決定するために取得する - あなたのnotificationsのすべてを収集し、その後のいずれか

  • 通知コレクションが含まれ、障害の種類を返す通知コレクション
  • を含んで例外をスロー

コントロールフローを例外または区別されたユニオンを処理するかどうかによって異なります。

+0

オブジェクトである 'Factory'は' Room'ですか?この 'RoomFactory(文字列名、文字列の説明)'では、 'Name'と' Description'オブジェクトを作成し、それぞれに 'hasError'をチェックして妥当性を判断します。それでも無効な不変量は許されませんか? – Unflux

0

notification.hasError関数を呼び出して後で例外をスローするとどうなりますか?

このようにして、通知処理を使用してエラーを処理することができます。例外のため、有効なオブジェクトがないことが保証されます。

+0

正確にはどこから 'notification.hasError()'を呼び出しますか? – Unflux

0

まあ、あなたはすでにそれをやっていましたが、1つのvalidate()メソッドですべての検証を共有すれば、ビジネスロジックを進める前にそれを呼び出すことができます。

だから私は、通知パターンを利用しますが、私のオブジェクトが無効な状態に入ることができないことを確実にすることについて得ただろうか

if (model.validate()) 
{ 
    // You can safely proceed 
} 

After this you can for example throw an exception so you will know that you have an object in an invalid state. 
+0

私は 'validate()'メソッドの呼び出しで自分のコードを丸めてしまうのを避けるため、 'constructor'でバリデーションを実行する別の方法があるかどうかを調べたいと思っています。 – Unflux

+0

あなたは何をしたいですか?あなたは通知パターンを見て、それだけでは不十分だと言いました。オブジェクトに無効なパラメータが指定されている場合、コンストラクタに例外をスローしようとしました。外部コードからパブリック検証メソッドを呼び出すだけでは不十分です。あなたが本当に欲しいのは何ですか? – tjugg

+0

これらのオプションは「十分ではありません」というわけではありません。一部の人にとってはこれらは完全に受け入れられ、 'model.validate()'を使用することになります。私は単に潜在的な代替案(もしあれば)を見て、学ぶだけです。私は何かを「方法」として受け入れることが好きではありません。それは純粋に効果があるからです。代替案があるかどうかを知りたいのです。 – Unflux

関連する問題