web-dev-qa-db-fra.com

Est-ce que renvoyer null bad design?

J'ai entendu des voix dire que rechercher une valeur nulle renvoyée par une méthode est une mauvaise conception. J'aimerais entendre certaines raisons pour cela.

pseudocode:

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

La raison pour ne pas renvoyer null est que vous n'avez pas à le vérifier et que votre code n'a donc pas besoin de suivre un chemin différent en fonction de la valeur de retour. Vous voudrez peut-être consulter/ Null Object Pattern qui fournit plus d'informations à ce sujet.

Par exemple, si je définissais une méthode en Java qui renvoyait une collection, je préférerais généralement renvoyer une collection vide (c'est-à-dire Collections.emptyList()) plutôt que la valeur null, car cela signifie que mon code client est plus propre. par exemple.

Collection<? extends Item> 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<? extends Item> c = getItems(); // Could potentially return null.

// Two possible code paths now so harder to test.
if (c != null) {
  for (Item item : c) {
    // Process item.
  }
}
191
Adamski

Voici la raison.

Dans Clean Code de Robert Martin il écrit que renvoyer null est une mauvaise conception alors que vous pouvez retourner, disons, 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. S'il s'agit d'un entier, peut-être que 0 suffira, s'il s'agit d'un hachage, un hachage vide. etc.

Le principe est de ne pas forcer le code d'appel à traiter immédiatement les problèmes. Le code d’appel peut ne pas vouloir s’occuper d’eux. C’est aussi pourquoi, dans de nombreux cas, les exceptions valent mieux que rien.

66
Max Chernyak

Bons usages du retour null:

  • Si null est un résultat functional valide, par exemple: FindFirstObjectThatNeedsProcessing () peut renvoyer null si non trouvé et l'appelant doit vérifier en conséquence.

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

  • attrape (...) et retourne null
  • L'initialisation de la dépendance à l'API a échoué
  • Espace disque insuffisant
  • Paramètres d'entrée non valides (erreur de programmation, les entrées doivent être vérifiées par l'appelant)
  • etc

Dans ces cas, lancer une exception est plus approprié car:

  • 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

Toutefois, Exceptions ne doit pas être utilisé pour gérer les conditions normales d’exécution du programme, telles que:

  • Nom d'utilisateur/mot de passe invalide (ou toute entrée fournie par l'utilisateur)
  • Rompre les boucles ou en tant que gotos non locaux
35
ZeroConcept

Oui, renvoyer NULL est un terrible design, dans un monde orienté objet. En un mot, l'utilisation de NULL conduit à:

  • traitement des erreurs ad-hoc (au lieu d'exceptions)
  • sémantique ambiguë
  • lent au lieu d'échec rapide
  • la pensée informatique au lieu de la pensée objet
  • objets mutables et incomplets

Vérifiez cette publication pour une explication détaillée: http://www.yegor256.com/2014/05/13/why-null-is-bad.html . Plus dans mon livre Elegant Objects , Section 4.1.

26
yegor256

Qui a dit que c'était un mauvais design?

La vérification des valeurs NULL est une pratique courante, même encouragée, sinon vous courez le risque d'exceptions NullReferenceExceptions partout. Il vaut mieux gérer l'erreur correctement que de lancer des exceptions lorsque vous n'en avez pas besoin.

22
Brandon

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

Renvoyer null depuis une méthode CreateWidget () semble mauvais. 

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

17
jcollum

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

12
Bahadır Yağan

Cela dépend de la langue que vous utilisez. Si vous utilisez un langage tel que C # où le moyen idiomatique d'indiquer l'absence de valeur consiste à renvoyer null, renvoyer null est donc une bonne conception si vous n'avez pas de valeur. Alternativement, dans des langages tels que Haskell qui utilisent idiomatiquement la monade Maybe, dans ce cas, renvoyer null serait une mauvaise conception (si cela était possible).

10
Greg Beech

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

Premièrement, lorsque quelque chose d'exceptionnel se produit (problème IO, etc.), des exceptions logiques sont générées. Quand exactement quelque chose est exceptionnel est probablement quelque chose pour un sujet différent .. 

Chaque fois qu'une méthode ne devrait avoir aucun résultat, il existe deux catégories:

  • S'il est possible de renvoyer une valeur neutre, faites-le.
    Énumrables vides, chaînes, etc. sont de bons exemples
  • Si une telle valeur neutre n'existe pas, null doit être renvoyé.
    Comme mentionné, la méthode est supposée n’avoir aucun résultat, elle n’est donc pas exceptionnelle et ne devrait donc pas renvoyer une 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 renvoyer null, j'essaie d'avoir une convention de dénomination pour le signaler.
Tout comme vous avez la conventionTry Something () pour les méthodes qui échouent, je nomme souvent mes méthodesSafe Quelque chose () lorsque la méthode retourne un résultat neutre au lieu de nul. 

Je ne suis pas encore complètement d'accord avec le nom, mais je ne peux rien trouver de mieux. Donc, je cours avec ça pour le moment.

5
Boris Callens

Les exceptions sont pour exceptiontoutes circonstances.

Si votre fonction est destinée à rechercher un attribut associé à un objet donné et que cet objet ne possède pas cet attribut, il peut être approprié de renvoyer null. Si l'objet n'existe pas, le lancement d'une exception peut être plus approprié. Si la fonction est censée renvoyer une liste d'attributs et qu'il n'y en a aucun à renvoyer, renvoyer une liste vide a du sens - vous renvoyez tous les attributs zéro.

3
HyperHacker

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

Pour les requêtes sur un seul article:

  • Create... renvoie une nouvelle instance, ou jette
  • Get... renvoie une instance existante attendue, ou renvoie
  • GetOrCreate... renvoie une instance existante ou une nouvelle instance s'il n'en existe aucune, ou renvoie
  • Find... renvoie une instance existante, si elle existe, ou null

Pour les requêtes de collection:

  • Get... renvoie toujours une collection, qui est 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 paramètres.

3
Johann Gerell

C'est bien de retourner la valeur null si cela a un sens:

public String getEmployeeName(int id){ ..}

Dans un cas comme celui-ci, renvoyer NULL si l'ID ne correspond à aucune entité existante peut s'avérer utile, car cela vous permet de distinguer le cas où aucune correspondance n'a été trouvée avec une erreur légitime.

Les gens peuvent penser que c'est mauvais parce que cela peut être utilisé comme une valeur de retour "spéciale" qui indique une condition d'erreur, ce qui n'est pas très bon, un peu comme retourner des codes d'erreur d'une fonction mais déroutant parce que l'utilisateur doit vérifier null, au lieu d'attraper les exceptions appropriées, par exemple.

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

Ce n'est pas nécessairement une mauvaise conception - comme avec beaucoup de décisions de conception, cela dépend.

Si le résultat de la méthode ne donne pas de bons résultats en utilisation normale, renvoyer null est correct:

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

S'il devrait toujours y avoir un résultat non nul, il pourrait ê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 /* ... */ {
}
3
Michael Burr

Pour certains scénarios, vous souhaitez remarquer un échec dès qu'il se produit.

Vérifier avec NULL et ne pas affirmer (pour les erreurs du programmeur) ou jeter (pour les erreurs de l'utilisateur ou de l'appelant) dans le cas d'échec peut signifier que les pannes ultérieures sont plus difficiles à repérer, car le cas impair d'origine n'a pas été trouvé.

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

2
Drew Hoskins

Quelles alternatives voyez-vous pour retourner null?

Je vois deux cas:

  • findAnItem (id). Que doit-on faire si l'article n'est pas trouvé?

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

  • listItemsMatching (critères) que doit-il renvoyer si rien n'est trouvé?

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

Je crois que return null peut être moins bon que les alternatives car il oblige le client à se rappeler de vérifier null, les programmeurs oublient et codent 

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

En Java, le lancement d’une exception vérifiée, RecordNotFoundException, permet au compilateur de rappeler au client de traiter le cas.

Je trouve que les recherches renvoyant des listes vides peuvent être très pratiques - il suffit de renseigner l’affichage avec tout le contenu de la liste, oh, il est vide, le code "fonctionne".

2
djna

Parfois, renvoyer NULL est la bonne chose à faire, mais en particulier lorsque vous avez affaire à des séquences de différentes sortes (tableaux, listes, chaînes, que vous avez), il est probablement préférable de renvoyer une séquence de longueur nulle, car conduit à un code plus court et, espérons-le, plus compréhensible, tout en ne prenant pas plus d'écrits de la part des développeurs d'API.

1
Vatine

Faites-les appeler une autre méthode après coup pour déterminer si l'appel précédent était nul. ;-) Hé, c'était assez bien pour JDBC

1
David Plumpton

L'idée de base derrière ce fil est de programmer de manière défensive. C'est-à-dire que le code contre l'inattendu . Il existe un tableau de réponses différentes:

Adamski suggère de regarder Null Object Pattern, cette réponse étant votée pour cette suggestion.

Michael Valenty suggère également une convention de nommage pour indiquer au développeur ce à quoi on peut s'attendre .ZeroConcept suggère une utilisation appropriée de Exception, si telle est la raison de NULL .

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 "créées" par un développeur: The Author

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

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

Si le développeur ne peut pas faire cela, alors la classe/méthode n'est pas déterministe . C'est-à-dire que si "l'appel de méthode" pour obtenir l'objet 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 l’OBJET NULL (par exemple, si (employé en tant que NullEmployee.ISVALID) doit être vérifié Et qu’il peut s’avérer nécessaire de recourir à une collection d’employés, l’approche par l’objet NULL 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 lève une exception supprime le choix du développeur de tester la validité de l'objet, ce qui est très mauvais pour une collection d'objets, et oblige le développeur à gérer les exceptions lors de la ramification de leur code consommateur.

En tant que développeur consommant la classe, j'espère que l'auteur me donnera la possibilité d'éviter ou de programmer pour la situation nulle que leur classe/méthodes peuvent être confrontés.

Donc, en tant que développeur, je programmerais de manière défensive contre NULL à partir d'une méthode . Si l'auteur m'a donné un contrat renvoyant toujours un objet (NULL OBJECT le fait toujours) Et que cet objet dispose d'une méthode/propriété permettant de tester la validité de l'objet, J'utiliserais alors cette méthode/cette propriété pour continuer à utiliser l'objet, sinon l'objet n'est pas valide .__ et je ne peux pas l'utiliser.

L’essentiel, c’est que l’auteur de la classe/des méthodes doit fournir les mécanismes que le 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 objets renvoyés À partir d'une autre classe/méthode.

cordialement 

GregJF

1
GregJF

Pour mon cas d'utilisation, je devais renvoyer une méthode Map from, puis rechercher une clé spécifique. Mais si je retourne une carte vide, cela mènera à NullPointerException et ce ne sera alors pas très différent, retournant null au lieu d'une carte 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.

0
Aman Gupta

Si le code est quelque chose comme:

command = get_something_to_do()

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

Si vous avez un objet factice dont la méthode execute () ne fait rien et que vous le retournez au lieu de Null dans les cas appropriés, vous n'avez pas à rechercher le cas Null et vous pouvez simplement effectuer les opérations suivantes:

get_something_to_do().execute()

Donc, ici, le problème ne réside pas entre la vérification de la valeur NULL et celle d'une exception, mais bien entre l'appelant qui doit gérer des non-cas spéciaux différemment (de quelque manière que ce soit) ou non.

0
Anon

Les autres options possibles sont: ne serait pas moins correct, puis renvoyer true/false et obtenir l'objet par paramètre.
Une autre approche consisterait à utiliser exception pour indiquer les échecs, mais ici - il y a en réalité beaucoup plus de voix qui disent que c'est une mauvaise pratique (car utiliser des exceptions peut être pratique mais présente de nombreux inconvénients).
Donc, personnellement, je ne vois rien de mal à renvoyer null comme indication que quelque chose ne va pas et à le vérifier plus tard (pour savoir réellement si vous avez réussi ou non). De plus, penser à l'aveuglette que votre méthode ne renverra pas la valeur NULL et basera votre code dessus peut entraîner d'autres erreurs, parfois difficiles à trouver, (bien que dans la plupart des cas, votre système plantera simplement :) 0x00000000 tôt ou tard).

0
Marcin Deptuła

Des fonctions nulles inattendues peuvent survenir lors du développement de programmes complexes et, tout comme le code mort, de telles occurrences indiquent de graves défauts dans la structure du 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'objet.

Null_function @wikipedia

0
Juicy Scripter

Cela dépend bien sûr du but de la méthode ... Parfois, un meilleur choix serait de lancer une exception. Tout dépend des cas.

0
Denis Biondic