Quel est le problème avec la validation des données et qu'est-ce que le principe de substitution de Liskov a à voir avec cela?



Si vous vous posez parfois la question: "est-ce que tout est bon pour moi dans cette méthode?" Et choisissez entre "et si ça souffle" et "il vaut mieux vérifier au cas où", alors bienvenue dans le ...

Correction: Comme lorc et 0xd34df00d l'ont noté , ce qui est décrit ci-dessous est appelé types dépendants. Vous pouvez les lire ici . Eh bien, voici le texte source avec mes réflexions à ce sujet.

Pendant le développement, il est souvent nécessaire de vérifier la validité des données pour certains algorithmes. Formellement, cela peut être décrit comme suit: supposons que nous obtenions une certaine structure de données, vérifions sa valeur pour la conformité avec une certaine plage de valeurs autorisées (ODZ) et la transmettons. Par la suite, la même structure de données peut être soumise à la même vérification. Si la structure reste inchangée, revérifier sa validité est évidemment une action inutile.

Bien que la validation puisse être vraiment longue, le problème n'est pas seulement lié aux performances. La responsabilité supplémentaire est beaucoup plus désagréable. Le développeur n'est pas sûr qu'il soit nécessaire de vérifier à nouveau la structure pour la validité. En plus d'une vérification inutile, au contraire, nous pouvons supposer l'absence de toute vérification, en supposant à tort que la structure a été vérifiée plus tôt.

Ainsi, les dysfonctionnements sont autorisés dans les méthodes qui attendent une structure éprouvée et ne fonctionnent pas correctement avec une structure dont la valeur est en dehors d'une certaine plage de valeurs acceptables.

C'est là que réside un problème plus évident et plus profond. En fait, une structure de données valide est un sous-type de la structure d'origine. De ce point de vue, le problème d'une méthode qui n'accepte que des objets valides est équivalent au code suivant dans un langage fictif:

class Parent { ... } class Child : Parent { ... } ... void processValidObject(Parent parent) { if (parent is Child) { // process } else { // error } } 

Convenez que maintenant le problème est beaucoup plus clair. Nous avons devant nous une violation canonique du principe de substitution de Liskov. Découvrez pourquoi violer le principe de substitution est mauvais, par exemple, ici .

Le problème du transfert d'objets invalides peut être résolu en créant un sous-type pour la structure de données d'origine. Par exemple, vous pouvez créer des objets via une fabrique qui renvoie soit un objet de sous-type valide, soit null selon la structure d'origine. Si nous changeons la signature des méthodes qui attendent une structure valide afin qu'elles n'acceptent qu'un sous-type, le problème disparaîtra. Outre la certitude que le système fonctionne exactement, le nombre de validations par centimètre carré de code diminuera. Un autre avantage est qu'avec de telles actions, nous transférons la responsabilité de la validation des données du développeur au compilateur.

Dans Swift, au niveau de la syntaxe, le problème de vérification de null est résolu. L'idée est de séparer les types en types nullables et non nullables. En même temps, il est fabriqué sous forme de sucre de telle sorte que le programmeur n'a pas besoin de déclarer un nouveau type. Lors de la déclaration d'un type de variable, ClassName garantit que la variable est différente de zéro et lors de la déclaration de ClassName? la variable est nulle. Dans le même temps, une covariance existe entre les types, c'est-à-dire que vous pouvez passer un objet de type ClassName aux méthodes qui acceptent ClassName?

Cette idée peut être étendue à un DLD défini par l'utilisateur. Fournir des objets avec des métadonnées contenant DLD stockées dans le type éliminera les problèmes décrits ci-dessus. Ce serait bien d'obtenir la prise en charge d'un tel outil dans un langage, mais ce comportement est également implémenté dans des langages OO "ordinaires", tels que Java ou C #, en utilisant l'héritage et une fabrique.

La situation de la validation des données est une autre confirmation que les entités de la POO ne sont pas prises du monde réel, mais des besoins d'ingénierie.

UPD: Comme indiqué correctement dans les commentaires, cela ne vaut la peine de créer des sous-types que si nous obtenons une fiabilité supplémentaire et réduisons le nombre de validations identiques.

De plus, l'article manque d'exemple. Laissez quelques chemins de fichiers nous arriver à l'entrée. Notre système fonctionne dans certains cas avec tous les fichiers, et dans certains cas uniquement avec les fichiers auxquels nous avons accès. Ensuite, nous voulons les transférer vers différents sous-systèmes, qui fonctionnent également avec des fichiers accessibles et inaccessibles. De plus, ces sous-systèmes transfèrent encore plus les fichiers, là où il n'est pas clair si le fichier est disponible ou non. Ainsi, en tout lieu douteux, un contrôle d'accès apparaîtra ou il pourra être oublié au contraire. Pour cette raison, le système deviendra plus compliqué en raison de l'ambiguïté et des contrôles généralisés. Mais ces vérifications chargent le disque et sont généralement lourdes. Vous pouvez mettre cette vérification en cache dans un champ booléen, mais cela ne nous épargnera pas le fait même de la nécessité d'une vérification. Je suggère de transférer la responsabilité de la vérification du développeur au compilateur.

Source: https://habr.com/ru/post/fr476220/


All Articles