Est-ce que renvoyer une mauvaise conception?

J’ai entendu des voix dire que la vérification d’une valeur nulle renvoyée par les méthodes est une mauvaise conception. Je voudrais entendre des raisons pour cela.

pseudocode:

variable x = object.method() if (x is null) do something 

La raison de ne pas renvoyer la valeur NULL est que vous n’avez pas à la rechercher et que, par conséquent, votre code n’a pas besoin de suivre un chemin différent en fonction de la valeur de retour. Vous pourriez vouloir vérifier le modèle d’object nul qui fournit plus d’informations à ce sujet.

Par exemple, si je devais définir une méthode en Java renvoyant une collection, je préférerais généralement renvoyer une collection vide (par exemple, Collections.emptyList() ) plutôt que null car cela signifie que mon code client est plus propre; par exemple

 Collection c = getItems(); // Will never return null. for (Item item : c) { // Will not enter the loop if c is empty. // Process item. } 

… qui est plus propre que:

 Collection c = getItems(); // Could potentially return null. // Two possible code paths now so harder to test. if (c != null) { for (Item item : c) { // Process item. } } 

Voici la raison.

Dans Clean Code de Robert Martin, il écrit que le renvoi de null est une mauvaise conception lorsque vous pouvez retourner, par exemple, un tableau vide. Puisque le résultat attendu est un tableau, pourquoi pas? Cela vous permettra d’itérer le résultat sans conditions supplémentaires. Si c’est un entier, peut-être que 0 suffira, si c’est un hachage, un hachage vide. etc.

La prémisse est de ne pas forcer le code d’appel à traiter immédiatement les problèmes. Le code d’appel peut ne pas vouloir s’en préoccuper. C’est aussi la raison pour laquelle, dans de nombreux cas, les exceptions sont meilleures que zéro.

Bons usages du retour null:

  • Si null est un résultat fonctionnel valide, par exemple: FindFirstObjectThatNeedsProcessing () peut renvoyer null s’il n’est pas trouvé et l’appelant doit vérifier en conséquence.

Mauvaises utilisations: essayer de remplacer ou de cacher des situations exceptionnelles telles que:

  • catch (…) et retourne null
  • L’initialisation de la dépendance de l’API a échoué
  • Espace disque insuffisant
  • Paramètres d’entrée incorrects (erreur de programmation, les entrées doivent être désinfectées par l’appelant)
  • etc

Dans ces cas, une exception est plus appropriée puisque:

  • Une valeur de retour nulle ne fournit aucune information d’erreur significative
  • L’appelant immédiat ne peut probablement pas gérer la condition d’erreur
  • Il n’y a aucune garantie que l’appelant vérifie les résultats nuls

Cependant, les exceptions ne doivent pas être utilisées pour gérer des conditions de fonctionnement normales du programme telles que:

  • Nom d’utilisateur / mot de passe invalide (ou toute entrée fournie par l’utilisateur)
  • Briser des boucles ou des gotos non locaux

Oui, le retour de NULL est une conception terrible , dans un monde orienté object. En résumé, l’utilisation de NULL mène à:

  • traitement des erreurs ad-hoc (au lieu des exceptions)
  • sémantique ambiguë
  • au lieu de perdre rapidement
  • la pensée informatique au lieu de penser object
  • objects mutables et incomplets

Consultez ce billet de blog pour une explication détaillée: http://www.yegor256.com/2014/05/13/why-null-is-bad.html . Plus dans mon livre Objets élégants , section 4.1.

Qui a dit que c’était une mauvaise conception?

Vérifier les valeurs NULL est une pratique courante, même encouragée, sinon vous courez le risque d’exceptions NullReferenceExceptions partout. Il est préférable de gérer l’erreur correctement que de lancer des exceptions lorsque vous n’en avez pas besoin.

D’après ce que vous avez dit jusqu’à présent, je pense qu’il n’y a pas assez d’informations.

Renvoyer null à partir d’une méthode CreateWidget () semble mauvais.

Renvoyer null à partir d’une méthode FindFooInBar () semble correct.

Son inventeur dit que c’est une erreur d’un milliard de dollars!

Cela dépend de la langue que vous utilisez. Si vous êtes dans un langage comme C # où la manière idiomatique d’indiquer l’absence de valeur est de renvoyer null, alors renvoyer null est une bonne conception si vous n’avez pas de valeur. Alternativement, dans des langages comme Haskell qui utilisent idiomatiquement la monade Maybe dans ce cas, alors renvoyer null serait une mauvaise conception (si cela était possible).

Si vous lisez toutes les réponses, il devient clair que la réponse à cette question dépend du type de méthode.

Tout d’abord, lorsque quelque chose d’exceptionnel se produit (IOproblem, etc.), des exceptions logiques sont levées. Quand exactement quelque chose est exceptionnel, c’est probablement quelque chose pour un autre sujet.

Chaque fois qu’une méthode est susceptible de ne pas avoir de résultats, il existe deux catégories:

  • S’il est possible de renvoyer une valeur neutre, faites-le .
    Les énumérateurs vides, les chaînes, etc. sont de bons exemples
  • Si une telle valeur neutre n’existe pas, la valeur null doit être renvoyée .
    Comme mentionné, la méthode est supposée ne pas avoir de résultat, elle n’est donc pas exceptionnelle et ne devrait donc pas lancer d’exception. Une valeur neutre n’est pas possible (par exemple: 0 n’est pas spécialement un résultat neutre, selon le programme)

Jusqu’à ce que nous ayons un moyen officiel de signaler qu’une fonction peut ou ne peut pas retourner la valeur NULL, j’essaye de définir une convention de dénomination.
Tout comme vous avez la convention Try Something () pour les méthodes qui devraient échouer, je nomme souvent mes méthodes Safe Something () lorsque la méthode renvoie un résultat neutre au lieu de null.

Je ne suis pas tout à fait d’accord avec le nom, mais je ne pouvais rien faire de mieux. Donc je cours avec ça pour le moment.

Il est bon de retourner null si cela a un sens:

 public Ssortingng getEmployeeName(int id){ ..} 

Dans un cas comme celui-ci, il est important de renvoyer null si l’ID ne correspond pas à une entité existante, car cela vous permet de distinguer le cas où aucune correspondance n’a été trouvée d’une erreur légitime.

Les gens peuvent penser que cela est mauvais car il peut être abusé comme une valeur de retour “spéciale” qui indique une condition d’erreur, ce qui n’est pas si bon, un peu comme retourner des codes d’erreur d’une fonction mais null, au lieu d’attraper les exceptions appropriées, par exemple

 public Integer getId(...){ try{ ... ; return id; } catch(Exception e){ return null;} } 

Les exceptions sont exceptionnelles .

Si votre fonction est destinée à rechercher un atsortingbut associé à un object donné, et que cet object ne possède pas un tel atsortingbut, il peut être approprié de renvoyer null. Si l’object n’existe pas, lancer une exception peut être plus approprié. Si la fonction est censée renvoyer une liste d’atsortingbuts et qu’il n’y en a pas à renvoyer, il est logique de renvoyer une liste vide – vous retournez tous les atsortingbuts zéro.

Ce n’est pas nécessairement une mauvaise conception – comme pour beaucoup de décisions de conception, cela dépend.

Si le résultat de la méthode est quelque chose qui n’aurait pas un bon résultat en utilisation normale, le retour de null est correct:

 object x = GetObjectFromCache(); // return null if it's not in the cache 

S’il devait toujours y avoir un résultat non nul, il serait peut-être préférable de lancer une exception:

 try { Controller c = GetController(); // the controller object is central to // the application. If we don't get one, // we're fubar // it's likely that it's OK to not have the try/catch since you won't // be able to really handle the problem here } catch /* ... */ { } 

Pour certains scénarios, vous souhaitez constater un échec dès que cela se produit.

Vérifier avec NULL et ne pas affirmer (pour les erreurs de programmeur) ou lancer (pour les erreurs utilisateur ou appelant) dans le cas d’échec peut signifier que les plantages ultérieurs sont plus difficiles à retrouver, car le cas impair original n’a pas été trouvé.

De plus, ignorer les erreurs peut mener à des exploits de sécurité. Peut-être que la nullité venait du fait qu’un tampon était écrasé ou similaire. Maintenant, vous ne tombez pas en panne, ce qui signifie que l’exploitant a une chance de s’exécuter dans votre code.

Quelles alternatives voyez-vous pour retourner null?

Je vois deux cas:

  • findAnItem (id). Que devrait-il faire si l’article n’est pas trouvé

Dans ce cas, nous pourrions: Renvoyer Null ou lancer une exception (cochée) (ou peut-être créer un élément et le renvoyer)

  • listItemsMatching (critère) qu’est-ce que cela devrait retourner si rien n’est trouvé?

Dans ce cas, nous pourrions renvoyer Null, renvoyer une liste vide ou lancer une exception.

Je pense que retourner null peut être moins bon que les alternatives car cela nécessite que le client se souvienne de vérifier null, les programmeurs oublient et codent

 x = find(); x.getField(); // bang null pointer exception 

En Java, lancer une exception vérifiée, RecordNotFoundException, permet au compilateur de rappeler au client de gérer le cas.

Je trouve que les recherches renvoyant des listes vides peuvent être très pratiques – il suffit de remplir l’affichage avec tout le contenu de la liste, oh c’est vide, le code “ne fonctionne que”.

J’ai une convention dans ce domaine qui m’a bien servi

Pour les requêtes à un seul article:

  • Create... renvoie une nouvelle instance ou lance
  • Get... renvoie une instance existante attendue ou lance
  • GetOrCreate... renvoie une instance existante ou une nouvelle instance si aucune n’existe ou lève
  • Find... renvoie une instance existante, si elle existe, ou null

Pour les requêtes de collecte:

  • Get... retourne toujours une collection vide si aucun élément [1] correspondant n’est trouvé

[1] étant donné certains critères, explicites ou implicites, donnés dans le nom de la fonction ou en tant que parameters.

L’idée de base derrière ce fil est de programmer de manière défensive. C’est-à-dire, le code contre l’inattendu. Il y a un éventail de réponses différentes:

Adamski suggère de regarder le modèle d’object nul, cette réponse étant mise aux voix pour cette suggestion.

Michael Valenty suggère également une convention de dénomination pour indiquer au développeur ce qui peut être attendu. ZeroConcept suggère une utilisation correcte d’Exception, si c’est la raison de la valeur NULL. Et d’autres.

Si nous faisons la “règle” que nous voulons toujours faire de la programmation défensive, alors nous pouvons voir que ces suggestions sont valables.

Mais nous avons 2 scénarios de développement.

Classes “authored” par un développeur: The Author

Classes “consommées” par un autre développeur (peut-être): le développeur

Que la classe retourne NULL ou non pour les méthodes avec une valeur de retour ou non, le développeur devra tester si l’object est valide.

Si le développeur ne peut pas faire cela, cette classe / méthode n’est pas déterministe. En d’autres termes, si “l’appel de méthode” pour obtenir l’object ne fait pas ce qu’il “annonce” (par exemple, getEmployee), il a rompu le contrat.

En tant qu’auteur d’une classe, je veux toujours être aussi gentil et défensif (et déterministe) lors de la création d’une méthode.

Donc, étant donné que NULL ou NULL OBJECT (par exemple, si (employé comme NullEmployee.ISVALID)) doit être vérifié et qu’il peut être nécessaire de le faire avec une collection d’employés, l’approche par object nul est la meilleure.

Mais j’aime aussi la suggestion de Michael Valenty de nommer la méthode qui DOIT renvoyer null, par exemple getEmployeeOrNull.

Un auteur qui émet une exception supprime le choix du développeur de tester la validité de l’object, ce qui est très mauvais pour une collection d’objects, et force le développeur à gérer les exceptions lors de la création de son code utilisateur.

En tant que développeur qui utilise la classe, j’espère que l’auteur me permet d’éviter ou de programmer la situation nulle à laquelle leur classe / méthode peut être confrontée.

Donc, en tant que développeur, je programmerais défensivement contre NULL à partir d’une méthode. Si l’auteur m’a donné un contrat qui renvoie toujours un object (NULL OBJECT le fait toujours) et que cet object possède une méthode / propriété permettant de tester la validité de l’object, j’utiliserais cette méthode / propriété pour continuer à utiliser l’object , sinon l’object n’est pas valide et je ne peux pas l’utiliser.

En bout de ligne, l’auteur de la classe / des méthodes doit fournir des mécanismes qu’un développeur peut utiliser dans sa programmation défensive. C’est-à-dire une intention plus claire de la méthode.

Le développeur doit toujours utiliser une programmation défensive pour tester la validité des objects renvoyés par une autre classe / méthode.

Cordialement

GregJF

Parfois, renvoyer NULL est la bonne chose à faire, mais en particulier lorsque vous avez affaire à des séquences de différents types (tableaux, listes, chaînes de caractères), il est probablement préférable de retourner une séquence de longueur nulle conduit à un code plus court et, espérons-le, plus compréhensible, tout en évitant d’écrire beaucoup plus sur la partie implémentation de l’API.

Faites-leur appeler une autre méthode après le fait pour déterminer si l’appel précédent était nul. 😉 Hey, c’était assez bon pour JDBC

D’autres options sont les suivantes: renvoyer une valeur indiquant le succès ou non (ou le type d’une erreur), mais si vous avez simplement besoin d’une valeur booléenne indiquant le succès / l’échec, renvoyer null en cas d’échec et un object à succès être moins correct, puis retourner true / false et obtenir l’object via le paramètre.
Une autre approche consisterait à utiliser l’exception pour indiquer les défaillances, mais ici, il y a en fait beaucoup plus de voix, qui disent que c’est une mauvaise pratique (car l’utilisation des exceptions peut être pratique mais présente de nombreux inconvénients).
Donc, personnellement, je ne vois rien de mal à renvoyer null en tant que preuve que quelque chose a mal tourné et à le vérifier plus tard (pour savoir si vous avez réussi ou non). De plus, penser aveuglément que votre méthode ne renverra pas NULL, puis baser votre code dessus, peut conduire à d’autres erreurs, parfois difficiles à trouver (bien que dans la plupart des cas, votre système sera bloqué :), comme vous vous en référez à 0x00000000 tôt ou tard).

Des fonctions nulles non intentionnelles peuvent survenir lors du développement de programmes complexes et, comme pour le code mort, de telles occurrences indiquent des failles graves dans les structures de programme.

Une fonction ou une méthode null est souvent utilisée comme comportement par défaut d’une fonction revectorable ou d’une méthode remplaçable dans une structure d’object.

Null_function @wikipedia

Bien sûr, cela dépend du but de la méthode … Parfois, un meilleur choix serait de lancer une exception. Tout dépend de cas en cas.

Si le code est quelque chose comme:

 command = get_something_to_do() if command: # if not Null command.execute() 

Si vous avez un object factice dont la méthode execute () ne fait rien, et que vous retournez cela à la place de Null dans les cas appropriés, vous n’avez pas à rechercher le cas Null et pouvez simplement faire:

 get_something_to_do().execute() 

Donc, ici, le problème ne se situe pas entre la vérification de NULL et une exception, mais plutôt entre l’appelant devant traiter les non-cas spéciaux différemment (de quelque manière que ce soit).

Pour mon cas d’utilisation, je devais renvoyer une méthode Map from, puis rechercher une clé spécifique. Mais si je retourne un Map vide, cela mènera à NullPointerException et il ne sera pas très différent de retourner null au lieu d’un Map vide. Mais à partir de Java8, nous pourrions utiliser Facultatif . Ce qui précède est la raison même pour laquelle le concept facultatif a été introduit.

G’day,

Renvoyer NULL lorsque vous ne parvenez pas à créer un nouvel object est une pratique standard pour de nombreuses API.

Pourquoi diable c’est mauvais design je n’en ai aucune idée.

Edit: Ceci est vrai pour les langues pour lesquelles vous ne disposez pas d’exceptions telles que C où il a été la convention pendant de nombreuses années.

HTH

‘Avahappy,