9
votes

Comment utiliser une validation réutilisable dans une valorisation

J'essaie de me mettre la tête alliant des techniques.

Il semble que la bonne pratique ne permet de ne jamais créer de valeurObject non valide. Le constructeur de valeursObject à cet effet devrait échouer chaque fois que le contenu fourni n'est pas suffisamment bon pour créer une valeur valideObject. Dans les exemples que j'ai, un objet EmailAddress ne peut être créé que lorsqu'il y a une valeur présente. Jusqu'à présent, si bon.

Validation de la valeur de la messagerie électronique fournie, c'est là que je commence à douter des principes. J'ai quatre exemples, mais je ne peux pas dire lequel devrait être considéré comme la meilleure pratique.

Exemple 1 est le simple: une fonction de construction, une "valeur" requise et une fonction distincte. Valider pour garder le code propre. Tout le code de validation reste à l'intérieur de la classe et ne sera jamais disponible dans le monde extérieur. La classe n'a qu'un seul but: stocker le courrier électronique et vous assurer qu'il ne sera jamais invalide. Mais le code ne sera jamais réutilisable - je crée un objet avec elle, mais c'est tout. xxx

exemple 2 fait la fonction de validation d'une fonction statique. La fonction ne changera jamais l'état de la classe. Il s'agit donc d'une utilisation correcte du mot clé statique, et le code de celui-ci ne sera jamais en mesure de modifier quoi que ce soit à une instance créée à partir de la classe d'intégration de la fonction statique. Mais si je veux réutiliser le code, je peux appeler la fonction statique. Néanmoins, cela me semble sale. xxx

exemple 3 introduit une autre classe, codée en dur à l'intérieur du corps de mon objet. L'autre classe est une classe de validation, contenant le code de validation et crée ainsi une classe pouvant être utilisée chaque fois que j'ai besoin d'une classe de validation. La classe elle-même est codée en dur, ce qui signifie également que je crée une dépendance sur cette classe de validation, qui devrait être toujours à proximité et n'est pas injectée par injection de dépendance. On pourrait dire qu'avoir un codé dur du validateur est aussi mauvais que le code complet intégré dans l'objet, mais d'autre part: DI est important, et cette façon doit créer une nouvelle classe (s'étendant ou simplement réécrire) à Changez simplement la dépendance. xxx

exemple 4 utilise à nouveau la classe de validateur, mais le met dans le constructeur. Mon ValueObject a donc besoin d'une classe de validateur déjà présente et créée avant de créer la classe, mais il est possible de remplacer facilement le validateur. Mais quelle est la qualité pour une classe de valeurObject simple d'avoir une telle dépendance dans le constructeur, car la seule chose vraiment importante est la valeur, elle ne devrait pas être ma préoccupation de savoir comment et où gérer si l'e-mail est correct et fournissait un validateur correct. xxx

Le dernier exemple que j'ai commencé à réfléchir, fournit un validateur par défaut et permettant d'injecter à travers DI un écrasement pour le validateur du constructeur . Mais j'ai commencé à douter à quel point une valeur de valorisation simple est quand vous écrasez la partie la plus importante: la validation.

Ainsi, tout le monde a une réponse à laquelle il faut mieux écrire cette classe, c'est correct pour quelque chose de plus facile En tant que courrier électronique, ou quelque chose de plus complexe comme un code à barres ou une carte de visa ou tout ce que l'on peut penser, et ne violera pas DDD, DI, OOP, Dry, mauvaise utilisation de statique, etc.

Le code complet: xxx


3 commentaires

Il y a une similaire Question si vous êtes intéressé


Si vous utilisez un validateur externe, votre objet de valeur n'est plus un objet de valeur. vous savez déjà quelle est la validation et sera toujours


Pourquoi pensez-vous que vous devrez réutiliser le code de validation de l'extérieur de cette classe? Le validateur est déjà le courriel e-mail . Si vous vous trouvez à utiliser le "code de validation" de l'extérieur de cette classe, alors vous faites probablement quelque chose de mal.


3 Réponses :


1
votes

Exemple 4!

Pourquoi? Parce que c'est testable, simple et simple. P>

Selon ce que votre validateur fait réellement (dans certaines circonstances, votre validateur peut s'appuyer sur un appel d'API ou un appel à une base de données), le validateur injectable est totalement testable via des moqueurs. Tous les autres sont soit impossibles à tester dans les circonstances que je viens de mentionner, soit incroyablement difficiles à tester. P>

Modifier: Strong> Pour ceux qui se demandent comment la méthode d'injection de dépendance aide alors Considérez la classe CommenterValidator ci-dessous qui utilise une bibliothèque de vérification de spam Akismet standard. p> xxx pré>

et maintenant ci-dessous, lorsque vous configurez vos tests d'unité, nous avons une classe de commentaire que nous utilisons Au lieu de cela, nous avons des setters pour modifier manuellement les 2 bools de sortie que nous pouvons avoir, et nous avons les 2 fonctions d'au-dessus de maquillages à la sortie de ce que nous voulons sans avoir à passer par l'API Akismet. P>

class CommentValidatorMock {
    public $lengthReturn = true;
    public $spamReturn = false;

    public function checkLength($text) {
        return $this->lengthReturn;
    }

    public function checkSpam($author, $email, $text, $link) {
        return $this->spamReturn;
    }

    public function setSpamReturn($val) {
        $this->spamReturn = $val;
    }

    public function setLengthReturn($val) {
        $this->lengthReturn = $val;
    }
}


6 commentaires

Et s'il y aurait quelque chose de mal à utiliser un validateur par défaut dans l'exemple 4, chaque fois que je ne soumettez aucun validateur avec le constructeur?


Je ne recommanderais pas de faire ça. J'aurais un autre chèque là-bas qui jette une exception si un validateur n'était pas passé à la place et que quelque chose ait attrapé et gérer cela. Je recommanderais de coller complètement avec le modèle DI et de ne pas boulonner d'autres morceaux.


Mais si j'écris complètement le code pour la validation dans la valorisation, ce serait bien de le faire? Dans ce cas, je peux toujours tester si une mauvaise adresse e-mail vous lance une erreur - et écrivez mes tests comme. Mais si j'utilise une classe externe pour faire la validation, je ne suis laissé qu'une seule option: fournissez toujours même le validateur par défaut via le constructeur et crée probablement toujours ma valeurObjects via une usine?


Il n'y a aucune raison pour laquelle cette option signifie que vous devez créer vos objets via une usine, c'est une décision de conception de votre part. Cependant, oui, vous devrez toujours fournir un validateur, bien que je ne puisse voir aucune raison pour que vous souhaitiez avoir une entrée de l'utilisateur qui n'était pas validée ... et si vous courez dans la question de devoir fournir Un validateur pour les données utilisateur renvoyées dans la base de données, vous ne devez peut-être pas avoir le validateur dans le constructeur si vous utilisez vos objets de cette manière.


Je n'ai pas particulièrement besoin du validateur, mais j'ai besoin de validation au moment où j'instaine l'objet - ce qui arrive au moment où je crée d'abord l'objet, qui se produira certainement la toute première fois avant de le donner à l'ormes. Chaque fois que je crée l'objet, il devrait être "valide", mais je continue à me demander si cette validation peut être mise en dehors de la classe ValueObject - et de la quatrième voie, j'ai besoin d'un conteneur di conteneur ou d'une simple usine ", provoque un svalidateur. La méthode permettrait de créer une valorisation non valideObject. Mais je ne suis pas sûr, une certaine confusion se passe-t-elle sur la question.


Les objets de valeur ne sont pas censés utiliser des usines, ils n'ont qu'une implémentation et elle est bien définie et constante



1
votes

Le premier instinct est généralement le meilleur. Vous devez utiliser la première option. E-mailAddress est un objet de valeur. Il peut être réutilisé dans d'autres objets ou entités de valeur. Je ne comprends pas pourquoi tu penses que ce n'est pas réutilisable. Vous pouvez avoir une "bibliothèque partagée" de ces objets de valeur communs utilisés dans d'autres contextes bornés. Faites attention à ce que vous mettez là-bas. Ils devraient être vraiment génériques si cela est même conceptuellement possible.


0 commentaires

0
votes

Je pense que si vous utilisez des méthodes de validation distinctes ou que vous déplacez les validateurs pour séparer la classe sera du beurre et empêchera Dry

    class EmailAddress{
      protected $value;
      public function __construct ($value)
      {
        $this->value = \validateEmailAddress($value);
      }
    }

    function validateEmailaddress(string $value) : string  
    {
      if(!is_string($value)){
        throw new \ValidationException('This is not an emailaddress.');
      } // Wrong function, just an example
      return $value;
    }

    //OR for strict OOP people
    final class VOValidator{
      private function __construct(){}
      public static function validateEmailaddress(string $input): string{...}
    }


    //I will prefer even go far and use Either from (FP monads) 

    interface ValueObejctError {}
    class InvalidEmail implements ValueObjectError {}

    function validateEmailaddress(string $input): Either { 
    // it will be better if php supported generic so using Either<InvalidaEmail, string> is more readable but unfortunately php has no generic types, maybe in future
      return is_string($input) 
        ? new Right($input)
        : new Left(new InvalidEmail());
    }


0 commentaires