Je cherche une recommandation ici. J'ai du mal à savoir s'il est préférable de renvoyer NULL ou une valeur vide d'une méthode lorsque la valeur de retour n'est pas présente ou ne peut pas être déterminée.
Prenez les deux méthodes suivantes à titre d'exemple:
string ReverseString(string stringToReverse) // takes a string and reverses it.
Person FindPerson(int personID) // finds a Person with a matching personID.
Dans In ReverseString()
, je dirais que return est une chaîne vide car le type de retour est string, donc l'appelant s'attend à cela. De même, l'appelant n'aurait pas à vérifier si une valeur NULL avait été renvoyée.
Dans FindPerson()
, retourner NULL semble être un meilleur ajustement. Indépendamment du new Person()
retour ou non de NULL ou d'un objet Personne vide ( ), l'appelant devra vérifier si l'objet Personne est vide ou NULL avant de lui appliquer quoi que ce soit (comme un appel UpdateName()
). Alors, pourquoi ne pas simplement renvoyer NULL ici et l’appelant n’a plus qu’à vérifier NULL.
Est-ce que quelqu'un d'autre a du mal avec ça? Toute aide ou perspicacité est appréciée.
Réponses:
StackOverflow a une bonne discussion sur ce sujet précis dans ce Q & A . Dans la question la mieux notée, Kronoz note:
Personnellement, j'aime bien renvoyer des chaînes vides pour les fonctions qui renvoient des chaînes afin de minimiser la quantité de traitement d'erreur à mettre en place. Cependant, vous devrez vous assurer que le groupe avec lequel vous travaillez suivra la même convention - sinon les avantages de cette décision ne seront pas réalisés.
Toutefois, comme l'a noté l'affiche dans la réponse à la SO, les valeurs NULL doivent probablement être renvoyées si un objet est attendu, de sorte qu'il ne fait aucun doute que les données sont renvoyées ou non.
En fin de compte, il n'y a pas de meilleure façon de faire les choses. Construire un consensus d’équipe conduira en définitive aux meilleures pratiques de votre équipe.
la source
for x in list_of_things() {...}
est sans doute plus rapide à parler alorsl = list_of_things(); if l != null {...}
emit(user.hometown)
queif user.hometown == null { emit("") else {emit(user.hometown)}
Dans tout le code que j'écris, j'évite de revenir
null
d'une fonction. J'ai lu cela dans Clean Code .Le problème avec l'utilisation
null
est que la personne qui utilise l'interface ne sait pas sinull
un résultat est possible, et si elle doit le vérifier, car il n'y a pas denot null
type de référence.En F #, vous pouvez renvoyer un
option
type, qui peut êtresome(Person)
ounone
, il est donc évident pour l'appelant qu'il doit vérifier.Le modèle (C) anti-analogue est la
Try...
méthode:Maintenant, je sais que des gens ont dit qu'ils détestaient le
Try...
motif parce qu'un paramètre de sortie casse les idées d'une fonction pure, mais ce n'est pas vraiment différent de ce qui suit:... et pour être honnête, vous pouvez supposer que tous les programmeurs .NET connaissent le
Try...
modèle, car il est utilisé en interne par le framework .NET. Cela signifie qu'ils n'ont pas besoin de lire la documentation pour comprendre ce qu'elle fait, ce qui est plus important pour moi que de s'en tenir au point de vue puriste des fonctions (comprendre qu'ilresult
s'agit d'unout
paramètre, pas d'unref
paramètre).Je vais donc avec
TryFindPerson
parce que vous semblez indiquer qu'il est parfaitement normal de ne pas pouvoir le trouver.Si, d’autre part, il n’ya aucune raison logique que l’appelant fournisse un message
personId
qui n’existait pas, je le ferais probablement:... et puis je lève une exception si elle était invalide. Le
Get...
préfixe implique que l'appelant sait qu'il doit réussir.la source
Vous pouvez essayer le modèle de cas spécial de Martin Fowler , tiré de Paterns of Enterprise Application Architecture :
la source
Je pense
ReverseString()
que renverrais la chaîne inversée et jetterait unIllegalArgumentException
si passé dans unNull
.Je pense que
FindPerson()
devrait suivre le modèle NullObject ou lever une exception non contrôlée sur le fait de ne pas trouver quelque chose si vous devriez toujours être capable de trouver quelque chose.Avoir à traiter
Null
est quelque chose qui devrait être évité. AvoirNull
dans les langues a été appelé une erreur d' un milliard de dollars par son inventeur!la source
Renvoyer une option . Tous les avantages de renvoyer une valeur non valide différente (par exemple, pouvoir avoir des valeurs vides dans vos collections) sans aucun risque de NullPointerException.
la source
boost::optional<T>
Je vois les deux côtés de cet argument, et je me rends compte que des voix assez influentes (par exemple, Fowler) préconisent de ne pas renvoyer les valeurs NULL afin de garder le code propre, d'éviter les blocs supplémentaires de traitement des erreurs, etc.
Cependant, j'ai tendance à me ranger du côté des partisans du renvoi nul. Je trouve qu'il y a une distinction importante dans l'invocation d'une méthode et que celle-ci répond avec je n'ai pas de données et qu'elle répond avec j'ai cette chaîne vide .
Depuis que j'ai vu une partie de la discussion référencer une classe Person, considérons le scénario dans lequel vous essayez de rechercher une instance de la classe. Si vous transmettez un attribut de recherche (par exemple, un ID), un client peut immédiatement vérifier la valeur null pour voir si aucune valeur n'a été trouvée. Ce n'est pas nécessairement exceptionnel (donc ne nécessitant pas d'exceptions), mais il devrait également être documenté clairement. Oui, cela demande une certaine rigueur de la part du client, et non, je ne pense pas que ce soit une mauvaise chose du tout.
Considérons maintenant l’alternative dans laquelle vous renvoyez un objet Personne valide… qui n’a rien. Mettez-vous des valeurs NULL dans toutes ses valeurs (nom, adresse, boisson favorite) ou allez-vous maintenant les renseigner avec des objets valides mais vides? Comment votre client peut-il maintenant déterminer qu'aucune personne réelle n'a été trouvée? Ont-ils besoin de vérifier si le nom est une chaîne vide au lieu de null? Ce genre de choses ne va-t-il pas réellement conduire à autant d'encombrement de code et d'instructions conditionnelles que si nous venions de vérifier la valeur null et de passer à autre chose?
Encore une fois, je suis d’accord avec certains arguments de cet argument, mais j’estime que c’est le plus logique pour la plupart des gens (rendre le code plus facile à gérer).
la source
FindPerson
ouGetPerson
renvoie-t-il une exception si la clé n'existe pas?" En tant qu'utilisateur de l'API, je ne le sais tout simplement pas et je dois vérifier la documentation. Par contre,bool TryGetPerson(Guid key, out Person person)
je n’exige pas que je vérifie la documentation et je sais qu’une exception n’est pas prévue dans ce qui équivaut à une circonstance plutôt non exceptionnelle. C'est ce que j'essaie de dire dans ma réponse.findPerson
), il est absolument normal de ne pas obtenir de résultat. Cela ne devrait pas conduire à une exception.Renvoie la valeur null si vous devez savoir si l'élément existe ou non. Sinon, renvoyez le type de données attendu. Cela est particulièrement vrai si vous retournez une liste d'éléments. Il est généralement prudent de supposer que si l'appelant veut une liste, il voudra parcourir la liste. Beaucoup de langues (la plupart? Toutes) échouent si vous essayez d'itérer sur null mais pas lorsque vous parcourez une liste vide.
Je trouve frustrant d'essayer d'utiliser un code comme celui-ci, mais de le faire échouer:
Je ne devrais pas avoir à ajouter un cas particulier dans le cas où il n'y a pas de choses .
la source
Cela dépend de la sémantique de la méthode. Vous pouvez éventuellement spécifier, votre méthode accepte uniquement "Non null". En Java, vous pouvez déclarer qu'en utilisant certaines annotations de métadonnées:
Vous devriez en quelque sorte spécifier la valeur de retour. Si vous déclarez que vous n'acceptez que les valeurs NotNull, vous êtes tenu de renvoyer une chaîne vide (si l'entrée était également une chaîne vide).
Le second cas est un peu compliqué dans sa sémantique. Si cette méthode consiste à renvoyer une personne par sa clé primaire (et que cette personne est censée exister), le meilleur moyen est de lancer une exception.
Si vous recherchez une personne par un identifiant supposé (ce qui me semble étrange), vous feriez mieux de déclarer cette méthode comme @Nullable.
la source
Je recommanderais d'utiliser le modèle d'objet nul chaque fois que possible. Cela simplifie le code lors de l’appel de vos méthodes, et vous ne pouvez pas lire un code laid comme
if (someObject != null && someObject.someMethod () != whateverValue)
Par exemple, si une méthode retourne une collection d'objets qui correspondent à un modèle, renvoyer une collection
null
vide aura plus de sens que de retourner , et itérer sur cette collection vide n'aura pratiquement aucune perte de performances. Un autre cas serait une méthode qui renvoie l'instance de la classe utilisée pour consigner les données, en renvoyant un objet Null plutôt que celanull
est préférable à mon avis, car cela ne force pas les utilisateurs à toujours vérifier si la référence renvoyée estnull
.Dans les cas où le retour a du
null
sens (en appelant lafindPerson ()
méthode par exemple), j'essaierais au moins de fournir une méthode qui retourne si l'objet est présent (personExists (int personId)
par exemple), un autre exemple serait lacontainsKey ()
méthodeMap
en Java). Cela rend le code plus propre pour les appelants, car vous pouvez facilement voir qu'il est possible que l'objet souhaité ne soit pas disponible (la personne n'existe pas, la clé n'est pas présente dans la carte). Vérifier constamment si une référence estnull
obscurcie le code à mon avis.la source
null est la meilleure chose à retourner si et seulement si les conditions suivantes s'appliquent:
De plus, assurez-vous de documenter le fait que la fonction peut renvoyer null.
Si vous suivez ces règles, les valeurs NULL sont généralement inoffensives. Notez que si vous oubliez de vérifier la valeur null, vous obtiendrez normalement une exception NullPointerException immédiatement après, ce qui est généralement un bogue assez facile à résoudre. Cette approche d'échec rapide est bien meilleure que d'avoir une fausse valeur de retour (par exemple une chaîne vide) qui se propage discrètement sur votre système, corrompant éventuellement les données, sans générer d'exception.
Enfin, si vous appliquez ces règles aux deux fonctions énumérées dans la question:
la source
À mon avis, il existe une différence entre renvoyer NULL, renvoyer un résultat vide (par exemple, la chaîne vide ou une liste vide) et renvoyer une exception.
Je prends normalement l'approche suivante. Je considère un appel de fonction ou de méthode f (v1, ..., vn) comme l'application d'une fonction
où S c'est "l'état du monde" T1, ..., Tn sont les types de paramètres d'entrée et T est le type de retour.
J'essaie d'abord de définir cette fonction. Si la fonction est partielle (c'est-à-dire qu'il y a des valeurs d'entrée pour lesquelles elle n'est pas définie), je retourne NULL pour le signaler. C'est parce que je veux que le calcul se termine normalement et me dire que la fonction que j'ai demandée n'est pas définie sur les entrées données. Utiliser, par exemple, une chaîne vide comme valeur de retour est ambigu, car il se peut que la fonction soit définie sur les entrées et que la chaîne vide corresponde au résultat correct.
Je pense que la vérification supplémentaire pour un pointeur NULL dans le code appelant est nécessaire parce que vous appliquez une fonction partielle et que la tâche de la méthode appelée vous indique si la fonction n'est pas définie pour l'entrée donnée.
Je préfère utiliser des exceptions pour les erreurs qui ne permettent pas d’effectuer le calcul (c’est-à-dire qu’il n’était pas possible de trouver une réponse).
Par exemple, supposons que j'ai une classe Client et que je souhaite implémenter une méthode.
pour rechercher un client dans la base de données d'application par son code. Dans cette méthode, je voudrais
Les vérifications supplémentaires pour null, par exemple
font partie de la sémantique de ce que je fais et je ne voudrais pas simplement les "sauter" afin de rendre le code plus lisible. Je ne pense pas que ce soit une bonne pratique de simplifier la sémantique du problème en question, simplement pour simplifier le code.
Bien sûr, puisque la vérification de la nullité est très fréquente, il est utile que le langage prenne en charge une syntaxe spéciale.
J'envisagerais également d'utiliser le modèle d'objet nul (comme suggéré par Laf) tant que je peux distinguer l'objet nul d'une classe de tous les autres objets.
la source
Les méthodes renvoyant des collections doivent retourner vides, mais d'autres peuvent renvoyer null, car pour les collections, il peut arriver que vous ayez un objet mais qu'il ne contienne aucun élément. L'appelant validera pour la taille plutôt que pour les deux.
la source
Pour moi, il y a deux cas ici. Si vous renvoyez une sorte de liste, vous devriez toujours la renvoyer quoi qu'il en soit, vide si vous n'avez rien à y insérer.
Le seul cas où je vois un débat, c'est lorsque vous retournez un seul article. Je me trouve préférant retourner null en cas d'échec dans une telle situation sur la base de rendre les choses échouer rapidement.
Si vous renvoyez un objet null et que l'appelant a besoin d'un objet réel, il est possible qu'il essaie d'utiliser l'objet nul en produisant un comportement inattendu. Je pense qu'il est préférable que la routine aille bon train s'ils oublient de gérer la situation. Si quelque chose ne va pas, je veux une exception dès que possible. Vous êtes moins susceptible d'envoyer un bug de cette façon.
la source
Ajoutant à ce que les gens ont déjà dit sur le
Maybe
constructeur de type:Un autre grand avantage, mis à part le fait de ne pas risquer de NPE, est le sémantique. Dans le monde fonctionnel, où nous traitons avec des fonctions pures, nous essayons de créer des fonctions qui, lorsqu'elles sont appliquées, soient exactement équivalentes à leur valeur de retour . Considérons le cas de
String.reverse()
: Ceci est une fonction qui donne une certaine chaîne comme la version inversée de cette chaîne. Nous savons que cette chaîne existe, car chaque chaîne peut être inversée (les chaînes sont essentiellement des ensembles de caractères ordonnés).Maintenant, qu'en est-il
findCustomer(int id)
? Cette fonction représente le client qui a l'identifiant donné. C'est quelque chose qui peut ou peut ne pas exister. Mais rappelez-vous que les fonctions ont une valeur de retour unique, vous ne pouvez donc pas vraiment dire "cette fonction renvoie un client avec l'ID donné OU elle le renvoienull
.Ceci est mon problème principal à la fois avec le retour
null
et le retour d'un objet null. Ils sont trompeurs.null
Ce n’est pas un client, et ce n’est pas vraiment "l’absence de client" non plus. C'est l'absence de RIEN. C'est juste un pointeur sans type vers RIEN. Très bas niveau et très moche et très sujette aux erreurs. Un objet nul est également assez trompeur ici, je pense. Un client nul est un client. Ce n'est pas l'absence d'un, ce n'est pas le client avec l'ID demandé, donc le renvoyer est simplement FAUX. Ce n'est pas le client que j'ai demandé, mais vous prétendez toujours que vous avez retourné un client. Il a le mauvais identifiant, il s'agit clairement d'un bug. Il rompt le contrat suggéré par le nom de la méthode et sa signature. Cela nécessite que le code client prenne en compte des éléments de votre conception ou lise de la documentation.Les deux problèmes sont résolus par a
Maybe Customer
. Du coup, nous ne disons pas que "cette fonction représente le client avec l'identifiant donné". Nous disons "cette fonction représente le client avec l'identifiant donné, s'il existe . Il est maintenant très clair et explicite sur son rôle. Si vous demandez le client avec un identifiant non existant, vous recevrezNothing
. Comment est-ce meilleur quenull
? pas seulement pour le risque de NPE. Mais aussi parce que le type deNothing
n'est pas seulementMaybe
. Il estMaybe Customer
. Il a un sens sémantique. cela signifie en particulier « l'absence d'un client », indiquant qu'un tel client n'existe pas.Un autre problème avec null est bien sûr qu'il est ambigu. Est-ce que vous n'avez pas trouvé le client, ou y a-t-il eu une erreur de connexion à la base de données ou est-ce que je ne suis pas autorisé à voir ce client?
Si vous avez plusieurs cas d'erreur de ce type à gérer, vous pouvez soit lancer des exceptions pour ces versions, soit (et je préfère de cette façon) renvoyer un
Either CustomerLoadError Customer
, représentant un problème ou le client retourné. Il présente tous les avantages deMaybe Customer
tout en vous permettant également de spécifier ce qui peut mal tourner.Je cherche surtout à saisir le contrat de la fonction dans sa signature. Ne présumez rien, ne vous fiez pas à des conventions éphémères, soyez explicite.
la source
1) Si la sémantique de la fonction est qu’elle ne peut rien renvoyer, l’appelant DOIT le tester, sinon il le fera par exemple. prenez votre argent et ne le donnez à personne.
2) Son bon de faire des fonctions qui sémantiquement retournent toujours quelque chose (ou jeter).
Avec un enregistreur , il est judicieux de renvoyer un enregistreur qui n'enregistre pas si aucun enregistreur n'a été défini. Lors du renvoi d'une collection, il n'est presque jamais logique (sauf à un "niveau suffisamment bas", où la collection elle-même est de contenir LES données, pas ce qu'elle contient) de ne rien renvoyer, car un ensemble vide est un ensemble, pas rien.
Avec un exemple de personne , j'utiliserais le mode "hibernation" (get vs load, IIRC, mais les choses sont compliquées par des objets proxy pour le chargement différé) de deux fonctions, une qui retourne la valeur null et l'autre qui lève.
la source
NULL doit être renvoyé si l'application s'attend à ce que des données soient disponibles mais que les données ne sont pas disponibles. Par exemple, un service qui renvoie la ville basée sur le code postal doit renvoyer null si la ville n’est pas trouvée. L’appelant peut alors décider de gérer le zéro ou d’exploser.
La liste vide doit être renvoyée s'il y a deux possibilités. Données disponibles ou AUCUNes données disponibles. Par exemple, un service qui renvoie la ou les villes si la population est supérieure à un certain nombre. Il peut retourner une liste vide si aucune donnée ne satisfait aux critères donnés.
la source
Retourner NULL est une conception terrible , dans un monde orienté objet. En un mot, l'utilisation de NULL conduit à:
Consultez ce billet de blog pour une explication détaillée: http://www.yegor256.com/2014/05/13/why-null-is-bad.html
la source