En supposant un langage avec une sécurité de type inhérente (par exemple, pas de JavaScript):
Avec une méthode qui accepte a SuperType
, nous savons que dans la plupart des cas, nous pourrions être tentés d'effectuer des tests de type pour choisir une action:
public void DoSomethingTo(SuperType o) {
if (o isa SubTypeA) {
o.doSomethingA()
} else {
o.doSomethingB();
}
}
Nous devrions généralement, si pas toujours, créer une seule méthode pouvant être remplacée par le SuperType
et faire ceci:
public void DoSomethingTo(SuperType o) {
o.doSomething();
}
... dans lequel chaque sous-type reçoit sa propre doSomething()
implémentation. Le reste de notre application peut alors ignorer de manière appropriée si une donnée SuperType
est vraiment un SubTypeA
ou SubTypeB
.
Merveilleux.
Mais nous avons toujours des is a
opérations similaires à celles de la plupart, sinon de la totalité, des langages sécurisés. Et cela suggère un besoin potentiel d'essais de type explicites.
Dans quelles situations devrions- nous ou devons- nous effectuer des tests de type explicites?
Pardonnez mon absence ou mon manque de créativité. Je sais que je l'ai déjà fait mais honnêtement, il y a si longtemps, je ne me souviens plus si ce que j'ai fait était bon! Et de mémoire récente, je ne pense pas avoir eu le besoin de tester des types en dehors de JavaScript, mon cow-boy.
la source
Réponses:
"Jamais", la réponse canonique à "quand est-ce que le test de type est acceptable?" Il n'y a aucun moyen de prouver ou d'infirmer cela. cela fait partie d'un système de croyances sur ce qui fait un "bon design" ou un "bon design orienté objet". C'est aussi hokum.
Pour être sûr, si vous avez un ensemble intégré de classes et plus d'une ou deux fonctions nécessitant ce type de test de type direct, vous êtes probablement en train de le faire. Ce dont vous avez vraiment besoin, c'est d'une méthode implémentée différemment dans
SuperType
ses sous-types. Cela fait partie intégrante de la programmation orientée objet, et les classes de la raison et l'héritage existent.Dans ce cas, explicitement, le test de type est faux, non pas parce que le test de type est intrinsèquement faux, mais parce que le langage a déjà une manière propre, extensible et idiomatique de réaliser la discrimination de type et que vous ne l'avez pas utilisé. Au lieu de cela, vous êtes tombé dans un idiome primitif, fragile et non extensible.
Solution: Utilisez l'idiome. Comme vous l'avez suggéré, ajoutez une méthode à chacune des classes, puis laissez les algorithmes d'héritage et de sélection de méthodes standard déterminer le cas qui s'applique. Ou si vous ne pouvez pas changer les types de base, sous-classe et ajoutez votre méthode ici.
Voilà pour la sagesse conventionnelle et quelques réponses. Quelques cas où le test de type explicite a du sens:
C'est une pièce unique. Si vous avez beaucoup de discrimination à faire, vous pouvez étendre les types ou la sous-classe. Mais tu ne le fais pas. Vous n'avez besoin que de tests explicites à un ou deux endroits. Par conséquent, il ne vaut pas la peine de revenir en arrière et de parcourir la hiérarchie des classes pour ajouter les fonctions en tant que méthodes. Ou bien, il ne vaut pas la peine, en pratique, d'ajouter le type de généralité, de test, de révision de conception, de documentation ou d'autres attributs des classes de base pour une utilisation aussi simple et limitée. Dans ce cas, l'ajout d'une fonction effectuant des tests directs est rationnel.
Vous ne pouvez pas ajuster les classes. Vous pensez au sous-classement - mais vous ne pouvez pas. Par exemple, de nombreuses classes en Java sont désignées
final
. Vous essayez de lancer apublic class ExtendedSubTypeA extends SubTypeA {...}
et le compilateur vous dit, en termes non équivoques, que ce que vous faites n'est pas possible. Désolé, grâce et sophistication du modèle orienté objet! Quelqu'un a décidé que vous ne pouvez pas étendre leurs types! Malheureusement, la plupart des bibliothèques standard le sontfinal
, et la création de classesfinal
est un guide de conception courant. Une fonction end-run est ce qui reste à votre disposition.BTW, cela ne se limite pas aux langages statiques. Langage dynamique Python a un certain nombre de classes de base qui, sous les couvertures implémentées en C, ne peuvent pas vraiment être modifiées. Comme Java, cela inclut la plupart des types standard.
Votre code est externe. Vous développez avec des classes et des objets provenant d'une gamme de serveurs de base de données, de moteurs middleware et d'autres bases de code que vous ne pouvez ni contrôler ni ajuster. Votre code n’est qu’un consommateur modeste d’objets générés ailleurs. Même si vous pouviez
SuperType
créer une sous - classe , vous ne pourrez pas obtenir les bibliothèques sur lesquelles vous comptez pour générer des objets dans vos sous-classes. Ils vont vous remettre des exemples des types qu’ils connaissent, pas vos variantes. Ce n'est pas toujours le cas ... parfois, ils sont conçus pour la flexibilité et ils instancient de manière dynamique les instances de classes que vous leur fournissez. Ou bien, ils fournissent un mécanisme pour enregistrer les sous-classes que vous souhaitez que leurs usines construisent. Les analyseurs syntaxiques XML semblent particulièrement efficaces pour fournir de tels points d’entrée; voir par exemple ou lxml en Python . Mais la plupart des bases de code ne fournissent pas de telles extensions. Ils vont vous rendre les cours pour lesquels ils ont été construits et qu'ils connaissent. En général, il n’est pas judicieux d’intégrer leurs résultats dans vos résultats personnalisés afin que vous puissiez utiliser un sélecteur de type uniquement orienté objet. Si vous voulez faire de la discrimination de type, vous devrez le faire de façon relativement grossière. Votre code de test de type semble alors tout à fait approprié.Générique du pauvre / envoi multiple. Vous souhaitez accepter une variété de types différents dans votre code et estimez qu'avoir un tableau de méthodes très spécifiques à un type n'est pas gracieux.
public void add(Object x)
semble logique, mais pas un tableau deaddByte
,addShort
,addInt
,addLong
,addFloat
,addDouble
,addBoolean
,addChar
, etaddString
variantes (pour ne citer que quelques - uns). Disposant de fonctions ou de méthodes qui prennent un super-type élevé, puis déterminent quoi faire, type par type, ils ne vous gagneront pas le Purity Award lors du symposium annuel Booch-Liskov Design, Naming hongrois vous donnera une API plus simple. Dans un sens, votreis-a
ouis-instance-of
testing simule une distribution générique ou multiple dans un contexte linguistique qui ne la prend pas en charge de manière native.La prise en charge intégrée de la langue pour les génériques et la saisie à l'aide de canards réduit le besoin de vérification de type en rendant plus probable la possibilité de "faire quelque chose de gracieux et approprié". La sélection de plusieurs envois / interfaces proposée dans des langues telles que Julia et Go remplace de la même manière les tests de type directs par des mécanismes intégrés pour la sélection de type "Que faire". Mais toutes les langues ne les supportent pas. Java, par exemple, est généralement à envoi unique, et ses idiomes ne sont pas très faciles à utiliser.
Mais même avec toutes ces fonctionnalités de discrimination de type - héritage, génériques, dactylographie et envoi multiple - il est parfois pratique de disposer d'une seule routine consolidée qui rend le fait que vous fassiez quelque chose en fonction du type d'objet. clair et immédiat. En métaprogrammation, je l’ai trouvé essentiellement inévitable. Que ce soit le type de pragmatisme en action ou de "codage en blanc" dépendra de votre philosophie de conception et de vos convictions.
la source
(1.0).Equals(1.0f)
céder true [argument favorise todouble
], mais(1.0f).Equals(1.0)
céder false [argument favorise toobject
]; en Java, onMath.round(123456789*1.0)
obtient 123456789, mais onMath.round(123456789*1.0)
obtient 123456792 [l'argument favorisefloat
plutôt quedouble
].Math.round
sont identiques à moi. Quelle est la différence?Math.round(123456789)
[indicatif de ce qui pourrait arriver si quelqu'un réécritMath.round(thing.getPosition() * COUNTS_PER_MIL)
pour renvoyer une valeur de position non mise à l'échelle, ne réalisant pas quegetPosition
int
long
La situation principale à laquelle j’ai eu besoin était lorsque l’on compare deux objets, comme dans une
equals(other)
méthode, qui peut nécessiter des algorithmes différents selon le type exact deother
. Même alors, c'est assez rare.L'autre situation dans laquelle j'ai eu à venir, encore une fois très rarement, est après la désérialisation ou l'analyse syntaxique, où vous en avez parfois besoin pour une conversion sécurisée vers un type plus spécifique.
De plus, vous avez parfois besoin d’un hack pour contourner un code tiers que vous ne contrôlez pas. C'est une de ces choses que vous ne voulez pas vraiment utiliser régulièrement, mais vous êtes content que ce soit là quand vous en avez vraiment besoin.
la source
BaseClass base = deserialize(input)
, parce que vous ne connaissez pas encore le type, vous devez alors leif (base instanceof Derived) derived = (Derived)base
stocker en tant que type dérivé exact.Le cas standard (mais espérons-le rare) ressemble à ceci: si dans la situation suivante
les fonctions
DoSomethingA
ouDoSomethingB
ne peuvent pas être facilement implémentées en tant que fonctions membres de l'arbre d'héritage deSuperType
/SubTypeA
/SubTypeB
. Par exemple, siDoSomethingXXX
à cette bibliothèque reviendrait à introduire une dépendance interdite.Notez qu'il existe souvent des situations où vous pouvez contourner ce problème (par exemple, en créant un wrapper ou un adaptateur pour
SubTypeA
etSubTypeB
ou, ou en essayant de leDoSomething
réimplémenter complètement en termes d'opérations de base deSuperType
), mais parfois ces solutions ne valent pas la peine des choses plus compliquées et moins extensibles que de faire le test de type explicite.Un exemple tiré de mon travail d’hier: j’étais dans une situation où j’allais paralléliser le traitement d’une liste d’objets (de type
SuperType
, avec exactement deux sous-types différents, où il est extrêmement improbable qu’il y en ait de plus). La version non parallèle contenait deux boucles: une boucle pour les objets du sous-type A appelantDoSomethingA
et une seconde boucle pour les objets du sous-type B appelantDoSomethingB
.Les méthodes "DoSomethingA" et "DoSomethingB" sont toutes deux des calculs gourmands en temps, qui utilisent des informations de contexte qui ne sont pas disponibles au niveau des sous-types A et B. (il est donc inutile de les implémenter en tant que fonctions membres des sous-types). Du point de vue de la nouvelle "boucle parallèle", cela facilite beaucoup les choses en les traitant de manière uniforme. J'ai donc implémenté une fonction similaire à celle
DoSomethingTo
d'en haut. Cependant, l'examen des implémentations de "DoSomethingA" et "DoSomethingB" montre qu'elles fonctionnent très différemment en interne. Donc, essayer de mettre en œuvre un "SuperType
quelque chose de générique" générique en utilisant beaucoup de méthodes abstraites ne fonctionnait pas vraiment, ou signifierait de sur-concevoir complètement les choses.la source
SuperType
et les sous - classes de elle?NSJSONSerialization
dans Obj-C), mais vous ne voulez pas simplement vous assurer que la réponse contient le type que vous attendez, vous devez donc le vérifier avant de l'utiliser (par exempleif ([theResponse isKindOfClass:[NSArray class]])...
) .Comme oncle Bob l'appelle:
Dans l'un de ses épisodes de Clean Coder, il a donné l'exemple d'un appel de fonction utilisé pour renvoyer l'
Employee
art.Manager
est un sous-type deEmployee
. Supposons que nous ayons un service d'application qui accepte unManager
identifiant et le convoque au bureau :) La fonctiongetEmployeeById()
renvoie un super-typeEmployee
, mais je souhaite vérifier si un responsable est renvoyé dans ce cas d'utilisation.Par exemple:
Ici, je vérifie si l’employé renvoyé par requête est en fait un responsable (c’est-à-dire que j’attends qu’il s’agisse d’un responsable et que, dans le cas contraire, il échoue rapidement).
Ce n'est pas le meilleur exemple, mais c'est l'oncle Bob après tout.
Mise à jour
J'ai mis à jour l'exemple autant que je m'en souvienne de mémoire.
la source
Manager
la mise en œuvre desummon()
simplement ne pas jeter l'exception dans cet exemple?CEO
peut appelerManager
.Employee
, il doit seulement veiller à obtenir quelque chose qui se comporte comme unEmployee
. Si différentes sous-classes deEmployee
ont des autorisations, des responsabilités, etc. différentes, pourquoi le test de type est-il une meilleure option qu'un système d'autorisations réel?Jamais.
is
clause, ou (dans certaines langues, ou en fonction de la scénario) car vous ne pouvez pas étendre le type sans modifier les éléments internes de la fonction effectuant lais
vérification.is
chèques sont un signe fort que vous violez le principe de substitution de Liskov . Tout ce qui fonctionne avecSuperType
devrait ignorer complètement les sous-types possibles.Cela dit, les
is
chèques peuvent être moins dommageables que les autres alternatives. Placer toutes les fonctionnalités communes dans une classe de base est fastidieux et conduit souvent à des problèmes plus graves. Utiliser une seule classe qui a un drapeau ou une énumération indiquant le "type" de l'instance ... est pire qu'horrible, car vous étendez maintenant le contournement du système de types à tous les consommateurs.En bref, vous devez toujours considérer les contrôles de type comme une odeur de code forte. Mais comme pour toutes les directives, il y aura des moments où vous serez obligé de choisir entre la violation de la directive la moins offensante.
la source
instanceof
fuit les détails de mise en œuvre et casse l'abstraction.IEnumerable<T>
ne promet pas un "dernier" élément existe. Si votre méthode a besoin d'un tel élément, elle devrait nécessiter un type qui en garantisse l'existence. Et les sous-types de ce type peuvent alors fournir des implémentations efficaces de la méthode "Last".Si vous avez une base de code volumineuse (plus de 100 000 lignes de code) et que vous êtes sur le point d'expédier ou si vous travaillez dans une branche qui devra être fusionnée par la suite, il y a donc un coût / risque considérable à changer beaucoup de gestion.
Vous avez alors parfois la possibilité d’utiliser un grand réfracteur du système ou un simple «test de type» localisé. Cela crée une dette technique qui devrait être remboursée le plus rapidement possible, mais ce n’est souvent pas le cas.
(Il est impossible de trouver un exemple, car tout code suffisamment petit pour être utilisé comme exemple est également suffisamment petit pour que la meilleure conception soit clairement visible.)
Ou en d'autres termes, lorsque l'objectif est de toucher votre salaire plutôt que d'obtenir «des votes” pour la propreté de votre conception.
L'autre cas courant est le code de l'interface utilisateur, par exemple lorsque vous affichez une interface utilisateur différente pour certains types d'employés, mais que vous ne souhaitez manifestement pas que les concepts d'interface utilisateur échappent à toutes vos classes de «domaine».
Vous pouvez utiliser le "test de type" pour choisir la version de l'interface utilisateur à afficher ou disposer d'une table de conversion sophistiquée convertissant les "classes de domaine" en "classes d'interface utilisateur". La table de consultation est juste un moyen de cacher le "test de type" en un seul endroit.
(Le code de mise à jour de la base de données peut poser les mêmes problèmes que le code de l'interface utilisateur. Cependant, vous n'avez généralement qu'un seul jeu de code de mise à jour de la base de données, mais vous pouvez avoir de nombreux écrans différents qui doivent s'adapter au type d'objet affiché.)
la source
L'implémentation de LINQ utilise de nombreuses vérifications de type pour les optimisations de performances possibles, puis un repli pour IEnumerable.
L’exemple le plus évident est probablement la méthode ElementAt (petit extrait de la source .NET 4.5):
Mais il existe de nombreux endroits dans la classe Enumerable où un modèle similaire est utilisé.
Alors peut-être que l'optimisation des performances pour un sous-type couramment utilisé est une utilisation valide. Je ne sais pas comment cela aurait pu être mieux conçu.
la source
IEnumerable<T>
incluant de nombreuses méthodes telles que celles deList<T>
, ainsi qu'uneFeatures
propriété indiquant les méthodes pouvant fonctionner normalement, lentement ou pas du tout. ainsi que diverses hypothèses qu'un consommateur peut faire en toute sécurité sur la collection (par exemple, sa taille et / ou son contenu existant sont-ils assurés de ne jamais changer [un type pourrait prendreAdd
en charge tout en garantissant que le contenu existant serait immuable]).Il existe un exemple qui apparaît souvent dans les développements de jeux, en particulier dans la détection de collision, qu'il est difficile de traiter sans l'utilisation d'une certaine forme de test de type.
Supposons que tous les objets du jeu proviennent d'une classe de base commune
GameObject
. Chaque objet a une forme de collision de corps rigideCollisionShape
qui peut fournir une interface commune ( - à - dire la position d'interrogation, orientation, etc.) , mais les formes de collision réels seront tous les sous - classes concrètes , telles queSphere
,Box
,ConvexHull
, etc. stocker spécifique de l' information pour ce type d'objet géométrique (voir ici pour un exemple réel)Maintenant, pour tester les collisions, je dois écrire une fonction pour chaque paire de types de formes de collision:
qui contiennent les calculs spécifiques nécessaires pour effectuer une intersection de ces deux types géométriques.
À chaque "tick" de ma boucle de jeu, je dois vérifier les paires d'objets pour les collisions. Mais je n'ai accès qu'aux
GameObject
s et aux s correspondantsCollisionShape
. Il est clair que j'ai besoin de connaître des types concrets pour savoir quelle fonction de détection de collision appeler. Pas même la double dépêche (qui n’est logiquement pas différente de la vérification du type) ne peut aider ici *.En pratique, dans cette situation, les moteurs physiques que j'ai vus (Bullet et Havok) reposent sur des tests de type d'une forme ou d'une autre.
Je ne dis pas que c'est nécessairement une bonne solution, c'est simplement que c'est peut-être la meilleure parmi un petit nombre de solutions possibles à ce problème.
* Techniquement, il est possible d’utiliser la double répartition d’une manière horrible et compliquée qui exigerait N (N + 1) / 2 combinaisons (où N est le nombre de types de formes que vous avez) et masquerait ce que vous faites réellement. recherche simultanément les types des deux formes, donc je ne considère pas cela comme une solution réaliste.
la source
Parfois, vous ne souhaitez pas ajouter une méthode commune à toutes les classes car il ne leur appartient en réalité pas d'exécuter cette tâche spécifique.
Par exemple, vous souhaitez dessiner certaines entités mais ne souhaitez pas leur ajouter directement le code de dessin (ce qui est logique). Dans les langues ne prenant pas en charge l'envoi multiple, vous pouvez vous retrouver avec le code suivant:
Cela devient problématique lorsque ce code apparaît à plusieurs endroits et que vous devez le modifier partout lorsque vous ajoutez un nouveau type d'entité. Si c'est le cas, vous pouvez éviter cela en utilisant le modèle Visiteur, mais il est parfois préférable de garder les choses simples et de ne pas trop les modifier. Ce sont les situations où le test de type est correct.
la source
Le seul moment que j'utilise est en combinaison avec la réflexion. Mais même dans ce cas, la vérification dynamique est généralement non codée en dur dans une classe spécifique (ou uniquement codée en dur dans des classes spéciales telles que
String
ouList
).Par vérification dynamique, je veux dire:
et non codé en dur
la source
Le test de type et le typage de type sont deux concepts étroitement liés. Si étroitement lié que je suis confiant de dire que vous ne devriez jamais faire de test de type sauf si votre intention est de dactylographier l'objet en fonction du résultat.
Lorsque vous pensez à une conception idéale orientée objet, les tests de type (et le transtypage) ne devraient jamais se produire. Mais avec un peu de chance, vous avez maintenant compris que la programmation orientée objet n'est pas idéale. Parfois, en particulier avec un code de niveau inférieur, le code ne peut rester fidèle à l'idéal. C'est le cas avec ArrayLists en Java; comme ils ne savent pas au moment de l'exécution quelle classe est stockée dans le tableau, ils créent des
Object[]
tableaux et les convertissent statiquement dans le type correct.Il a été souligné qu'un besoin courant de tests de typage (et de transtypage) provient de la
Equals
méthode, qui dans la plupart des langues est supposée prendre une formeObject
. L'implémentation doit prévoir des vérifications détaillées si les deux objets sont du même type, ce qui nécessite de pouvoir tester leur type.Les essais de type sont également fréquemment évoqués. Vous aurez souvent des méthodes qui renvoient
Object[]
ou un autre tableau générique, et vous voulez extraire tous lesFoo
objets pour une raison quelconque. Ceci est une utilisation parfaitement légitime des tests de type et de la conversion.En général, le test de type est mauvais lorsqu'il couple inutilement votre code à la manière dont une implémentation spécifique a été écrite. Cela peut facilement nécessiter un test spécifique pour chaque type ou combinaison de types, par exemple si vous souhaitez rechercher l'intersection de lignes, de rectangles et de cercles, et que la fonction d'intersection utilise un algorithme différent pour chaque combinaison. Votre objectif est de placer tous les détails spécifiques à un type d'objet au même endroit que cet objet, car cela facilitera la maintenance et l'extension de votre code.
la source
ArrayLists
Je ne connais pas la classe stockée au moment de l'exécution car Java ne contenait pas de génériques et quand ils ont finalement été introduits, Oracle a opté pour une compatibilité ascendante avec du code sans génériques.equals
a le même problème, et c'est une décision de conception discutable de toute façon; les comparaisons d'égalité n'ont pas de sens pour tous les types.String x = (String) myListOfStrings.get(0)
Object
été difficile d’y accéder; les génériques en Java ne fournissent que le casting implicite sécurisé par les règles du compilateur.C'est acceptable dans le cas où vous devez prendre une décision impliquant deux types et que cette décision est encapsulée dans un objet situé en dehors de cette hiérarchie de types. Par exemple, supposons que vous planifiez le prochain objet traité dans une liste d'objets en attente de traitement:
Maintenant, disons que notre logique commerciale est littéralement "toutes les voitures ont la priorité sur les bateaux et les camions". L'ajout d'une
Priority
propriété à la classe ne vous permet pas d'exprimer clairement cette logique métier, car vous obtiendrez ceci:Le problème est que, maintenant, pour comprendre l'ordre de priorité, vous devez examiner toutes les sous-classes ou, en d'autres termes, vous avez ajouté le couplage aux sous-classes.
Vous devez bien sûr définir les priorités en constantes et les placer dans une classe par elles-mêmes, ce qui permet de maintenir la logique métier de planification ensemble:
Cependant, en réalité, l’algorithme de planification est quelque chose qui pourrait changer à l’avenir et il pourrait éventuellement dépendre de plus que de la simple saisie. Par exemple, on pourrait dire que "les camions pesant plus de 5 000 kg ont priorité sur tous les autres véhicules". C'est pourquoi l'algorithme de planification appartient à sa propre classe et il est judicieux de vérifier le type pour déterminer lequel doit être utilisé en premier:
C'est le moyen le plus simple d'implémenter la logique métier et reste le plus flexible pour les modifications futures.
la source
null
unString
, ou unString[]
. Si 99% des objets nécessitent exactement une chaîne, l'encapsulation de chaque chaîne dans une construction séparéeString[]
peut ajouter une surcharge de stockage. Gérer le cas de chaîne unique en utilisant une référence directe à aString
nécessitera plus de code, mais économisera de la mémoire et accélérera les choses.Le test de type est un outil, utilisez-le à bon escient et cela peut être un puissant allié. Utilisez-le mal et votre code va commencer à sentir.
Dans notre logiciel, nous avons reçu des messages sur le réseau en réponse à des demandes. Tous les messages désérialisés partageaient une classe de base commune
Message
.Les classes elles-mêmes étaient très simples, juste la charge utile telle que les propriétés C # typées et les routines permettant de les regrouper et de les hiérarchiser (En fait, j'ai généré la plupart des classes à l'aide de modèles t4 à partir de la description XML du format du message)
Le code serait quelque chose comme:
Certes, on pourrait soutenir que l’architecture du message pourrait être mieux conçue, mais elle a été conçue il ya longtemps et non pour C #, c’est donc ce qu’elle est. Ici, le test de type a résolu un problème réel pour nous d'une manière pas trop chaotique.
Il est à noter que C # 7.0 est en train d’obtenir une correspondance de motif (ce qui, à bien des égards, est un test de type sur stéroïdes) ne peut pas être si mauvais ...
la source
Prenez un analyseur JSON générique. Le résultat d'une analyse réussie est un tableau, un dictionnaire, une chaîne, un nombre, un booléen ou une valeur null. Ce peut être n'importe lequel de ceux-là. Et les éléments d'un tableau ou les valeurs d'un dictionnaire peuvent à nouveau être l'un de ces types. Étant donné que les données proviennent de l'extérieur de votre programme, vous devez accepter tous les résultats (c'est-à-dire que vous devez les accepter sans planter; vous pouvez rejeter un résultat qui ne correspond pas à vos attentes).
la source