Regardons les options, où nous pouvons placer le code de validation:
- À l'intérieur des setters dans le constructeur.
- Dans la
build()
méthode.
- À l'intérieur de l'entité construite: il sera invoqué dans la
build()
méthode lors de la création de l'entité.
L'option 1 nous permet de détecter les problèmes plus tôt, mais il peut y avoir des cas compliqués où nous pouvons valider une entrée ayant uniquement le contexte complet, réalisant ainsi au moins une partie de la validation dans la build()
méthode. Ainsi, le choix de l'option 1 entraînera une incohérence du code, une partie de la validation étant effectuée à un endroit et une autre à un autre endroit.
L'option 2 n'est pas bien pire que l'option 1, car, en règle générale, les paramètres de configuration dans le générateur sont appelés juste avant les build()
interfaces particulièrement fluides. Ainsi, il est toujours possible de détecter un problème suffisamment tôt dans la plupart des cas. Toutefois, si le générateur n'est pas le seul moyen de créer un objet, cela entraînera une duplication du code de validation, car vous devrez l'avoir partout où vous créez un objet. La solution la plus logique dans ce cas sera de placer la validation le plus près possible de l’objet créé, c’est-à-dire à l’intérieur de celui-ci. Et c'est l' option 3 .
Du point de vue de SOLID, placer la validation dans le générateur constitue également une violation de SRP: la classe de générateur a déjà la responsabilité d'agréger les données pour construire un objet. La validation consiste à établir des contrats sur son propre état interne. Il est de la nouvelle responsabilité de vérifier l'état d'un autre objet.
Ainsi, de mon point de vue, non seulement il vaut mieux échouer tard du point de vue de la conception, mais il est également préférable d'échouer à l'intérieur de l'entité construite, plutôt que dans le constructeur lui-même.
UPD: ce commentaire m'a rappelé une possibilité supplémentaire, lorsque la validation à l'intérieur du constructeur (option 1 ou 2) est logique. Cela a du sens si le constructeur a ses propres contrats sur les objets qu'il crée. Par exemple, supposons que nous ayons un générateur qui construit une chaîne avec un contenu spécifique, par exemple une liste de plages de nombres 1-2,3-4,5-6
. Ce constructeur peut avoir une méthode comme addRange(int min, int max)
. La chaîne résultante ne sait rien de ces chiffres, elle ne devrait pas non plus avoir à le savoir. Le constructeur lui-même définit le format de la chaîne et les contraintes sur les nombres. Ainsi, la méthode addRange(int,int)
doit valider les nombres saisis et lever une exception si max est inférieur à min.
Cela dit, la règle générale sera de ne valider que les contrats définis par le constructeur lui-même.