Est-il préférable de renvoyer des valeurs NULL ou vides à partir de fonctions / méthodes où la valeur de retour n'est pas présente?


135

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.


2
Ça va varier. Vous pouvez également faire valoir que la méthode doit arrêter les valeurs incorrectes à l’avance, par exemple le paramètre stringToReverse doit être transmis vide ou null. Si vous avez effectué cette vérification (renvoyé une exception), il retournera toujours autre chose que null.
Aaron McIver

Fowler POEAA - p. 496 Modèles de base - Cas particulier (objet null) Bloch Effective Java, 2e édition Elément 43: Retourne des tableaux ou des collections vides, et non des nuls
Boris Treukhov

1
@ Boris Je n'ai pas vu votre commentaire. Je viens de poster un cas spécial comme réponse.
Thomas Owens

@Thomas, je n'y vois aucun problème :). En ce qui concerne la question, c’est de toute façon une question de bon sens.
Boris Treukhov

Un commentaire intéressant est que, dans les bases de données Oracle, NULL et la chaîne vide sont identiques
WuHoUnited

Réponses:


77

StackOverflow a une bonne discussion sur ce sujet précis dans ce Q & A . Dans la question la mieux notée, Kronoz note:

Renvoyer une valeur nulle est généralement la meilleure idée si vous souhaitez indiquer qu'aucune donnée n'est disponible.

Un objet vide implique que des données ont été renvoyées, alors que renvoyer null indique clairement que rien n'a été renvoyé.

De plus, renvoyer une valeur null entraînera une exception null si vous tentez d'accéder aux membres de l'objet, ce qui peut être utile pour mettre en évidence un code erroné - tenter d'accéder à un membre dépourvu de sens n'a aucun sens. L'accès aux membres d'un objet vide n'échouera pas, ce qui signifie que les bogues peuvent rester non découverts.

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.


8
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. Jamais compris cet argument. Le chèque nul est quoi, au plus <10 caractères? Pourquoi agissons-nous comme s'il s'agissait d'un travail de flexion?
Aaron McIver

19
Personne n'a dit qu'il s'agissait d'un travail de flexion. Mais il est le travail. Il ajoute un cas particulier au code, ce qui signifie une branche supplémentaire à tester. De plus, cela perturbe le flux du code. for x in list_of_things() {...}est sans doute plus rapide à parler alorsl = list_of_things(); if l != null {...}
Bryan Oakley

5
@ Bryan: il existe une grande différence entre une liste vide de valeurs et une chaîne vide. Une chaîne représente rarement une liste de caractères.
kevin cline

3
@ Kevin Cline: bien sûr. Mais dans le cas d'une chaîne, vous pouvez souhaiter que cette chaîne apparaisse dans un journal, qu'elle soit nulle ou non. Devoir vérifier si elle est nulle pour pouvoir imprimer une chaîne vide obscurcit l'intention réelle. Ou peut-être que c'est quelque chose comme un champ de base de données contenant le contenu ajouté par l'utilisateur que vous souhaitez ajouter à une page Web. C'est plus facile à faire emit(user.hometown)queif user.hometown == null { emit("") else {emit(user.hometown)}
Bryan Oakley

1
Non seulement c'est une saisie supplémentaire (pas très grave), mais cela affecte la lisibilité. Personnellement, je trouve le code avec un tas de contrôles nuls distrayant.
ToddR

82

Dans tout le code que j'écris, j'évite de revenir nulld'une fonction. J'ai lu cela dans Clean Code .

Le problème avec l'utilisation nullest que la personne qui utilise l'interface ne sait pas si nullun résultat est possible, et si elle doit le vérifier, car il n'y a pas de not nulltype de référence.

En F #, vous pouvez renvoyer un optiontype, qui peut être some(Person)ou none, il est donc évident pour l'appelant qu'il doit vérifier.

Le modèle (C) anti-analogue est la Try...méthode:

public bool TryFindPerson(int personId, out Person result);

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:

class FindResult<T>
{
   public FindResult(bool found, T result)
   {
       this.Found = found;
       this.Result = result;
   }

   public bool Found { get; private set; }
   // Only valid if Found is true
   public T Result { get; private set;
}

public FindResult<Person> FindPerson(int personId);

... 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'il results'agit d'un outparamètre, pas d'un refparamètre).

Je vais donc avec TryFindPersonparce 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 personIdqui n’existait pas, je le ferais probablement:

public Person GetPerson(int personId);

... 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.


28
+1, je répondrais à cette question par une référence à Clean Code si vous ne l'aviez pas déjà fait. J'aime le mantra: "SI VOTRE FONCTION NE PEUT PAS FAIRE QUEL SON NOM EST DIT, ALORS LANCE UNE EXCEPTION." Muuuch vaut mieux que de vérifier null toutes les douzaines de lignes ou plus ....
Graham

13
J'ai renoncé à assumer quoi que ce soit au sujet de la connaissance des gens.
CaffGeek

5
"Le problème avec l'utilisation de null est que la personne qui utilise l'interface ne sait pas si la null est un résultat possible, et si elle doit la vérifier, car il n'y a pas de type de référence non null." Étant donné que les types de référence peuvent être nuls, ne pouvez-vous pas toujours supposer que NULL est un résultat possible?
Tommy Carlier

4
Si une fonction renvoie un pointeur (C / C ++) ou une référence à un objet (Java), je suppose toujours qu'elle peut renvoyer null.
Giorgio

7
"Le problème avec l'utilisation de null est que la personne utilisant l'interface ne sait pas si null est un résultat possible, et si elle doit le vérifier, [...]" Si une méthode peut renvoyer null, elle doit être explicitement mentionné dans le contrat API.
Zsolt Török

28

Vous pouvez essayer le modèle de cas spécial de Martin Fowler , tiré de Paterns of Enterprise Application Architecture :

Les null sont des choses gênantes dans les programmes orientés objet, car elles combattent le polymorphisme. Habituellement, vous pouvez appeler librement foo sur une référence de variable d'un type donné sans vous soucier de savoir si l'élément est le type exact ou une sous-classe. Avec un langage fortement typé, vous pouvez même demander au compilateur de vérifier que l'appel est correct. Cependant, comme une variable peut contenir null, vous pouvez rencontrer une erreur d'exécution en appelant un message sur null, ce qui vous donnera une trace de pile agréable et conviviale.

S'il est possible qu'une variable soit NULL, vous devez vous rappeler de l'entourer de code de test NULL afin que vous fassiez ce qu'il faut si une valeur NULL est présente. Souvent, la bonne chose est la même dans de nombreux contextes. Vous finissez donc par écrire du code similaire dans de nombreux endroits, en commettant le péché de la duplication de code.

Les nullités sont un exemple courant de tels problèmes et d’autres apparaissent régulièrement. Dans les systèmes de nombres, vous devez faire face à l'infini, qui a des règles spéciales pour des choses telles que l'addition qui cassent les invariants habituels des nombres réels. L'une de mes premières expériences dans le domaine des logiciels de gestion a été avec un client du service public qui n'était pas connu, appelé "occupant". Tout cela implique de modifier le comportement habituel du type.

Au lieu de renvoyer null ou une valeur étrange, renvoyez un cas spécial ayant la même interface que celle attendue par l'appelant.


J'ai rencontré ce scénario, mais seulement après la publication du logiciel. Mes données de test ne l'ont jamais déclenchée, donc c'était un peu à l'improviste et une douleur à déboguer. Bien que je ne l'ait pas résolu avec le motif Cas particulier, il est bon de le savoir.
samis

14

Je pense ReverseString()que renverrais la chaîne inversée et jetterait un IllegalArgumentExceptionsi passé dans un Null.

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 Nullest quelque chose qui devrait être évité. Avoir Nulldans les langues a été appelé une erreur d' un milliard de dollars par son inventeur!

Je l'appelle mon erreur d'un milliard de dollars. C’était l’invention de la référence null en 1965. À cette époque, je concevais le premier système de types complet pour les références dans un langage orienté objet (ALGOL W).

Mon objectif était de veiller à ce que toute utilisation des références soit absolument sûre, avec une vérification automatique par le compilateur. Mais je ne pouvais pas résister à la tentation de mettre une référence nulle, tout simplement parce que c'était si facile à mettre en œuvre.

Cela a entraîné d'innombrables erreurs, vulnérabilités et pannes système, qui ont probablement causé des millions de dollars de douleur et de dommages au cours des quarante dernières années.

Ces dernières années, plusieurs analyseurs de programme tels que PREfix et PREfast de Microsoft ont été utilisés pour vérifier les références et émettre des avertissements s’ils risquaient d’être non nuls. Des langages de programmation plus récents tels que Spec # ont introduit des déclarations pour des références non nulles. C’est la solution que j’ai rejetée en 1965.

Tony Hoare


11

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.


1
Intéressant. Comment implémenter une option en Java? Et en C ++?
Giorgio

1
@Giorgio, Pour Java, les bibliothèques Guava, de Google, ont une classe appelée Optional .
Otavio Macedo

1
Pour C ++, il y aboost::optional<T>
MSalters le

8

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 différence est la suivante: " retourne-t-il une valeur nulle FindPersonou GetPersonrenvoie-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.
Scott Whitlock

4
Je pense que les gens sont trop attachés aux exceptions. Les exceptions sont mauvaises et consomment beaucoup de ressources. Sans parler des gens essayent et attrapent des déclarations sans rien résoudre. Les exceptions indiquent clairement que quelque chose s'est fondamentalement mal passé. Par exemple, les références null lèvent une exception, car il est clairement erroné de faire référence à des objets null. Il est faux de l'utiliser pour vérifier si une personne a été trouvée ou non.
Vladimir Kocjancic

1
@ VladimirKocjancic: Je suis tout à fait d'accord: les exceptions devraient être utilisées pour des situations exceptionnelles, telles que des bugs logiciels, des défaillances matérielles, une mauvaise configuration du système. Si vous calculez une fonction partielle (comme findPerson), il est absolument normal de ne pas obtenir de résultat. Cela ne devrait pas conduire à une exception.
Giorgio

6

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:

for thing in get_list_of_things() {
    do_something_clever(thing)
}

Je ne devrais pas avoir à ajouter un cas particulier dans le cas où il n'y a pas de choses .


7
Dans les cas où une liste ou un autre groupe de valeurs est attendu, je choisis toujours de renvoyer une liste vide, exactement pour cette raison. L'action par défaut (itération sur une liste vide) est presque toujours correcte. Si ce n'est pas le cas, l'appelant peut vérifier explicitement si la liste est vide.
TMN

5

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:

string ReverseString(@NotNull String stringToReverse)

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.

Person FindPerson(int personID)

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.


3

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 nullvide 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 cela nullest préférable à mon avis, car cela ne force pas les utilisateurs à toujours vérifier si la référence renvoyée est null.

Dans les cas où le retour a du nullsens (en appelant la findPerson ()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 la containsKey ()méthode Mapen 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 est nullobscurcie le code à mon avis.


3

null est la meilleure chose à retourner si et seulement si les conditions suivantes s'appliquent:

  • le résultat nul est attendu en fonctionnement normal . On pourrait s’attendre à ce que vous ne puissiez pas trouver une personne dans certaines circonstances raisonnables. Par conséquent, trouverPerson () renvoyant la valeur null convient. Toutefois, s’il s’agit d’un échec véritablement inattendu (par exemple, dans une fonction appelée saveMyImportantData ()), vous devriez alors lancer une exception. Il y a un indice dans le nom - Les exceptions sont pour des circonstances exceptionnelles!
  • NULL signifie "non trouvé / aucune valeur" . Si vous voulez dire autre chose, alors retournez autre chose! De bons exemples sont les opérations en virgule flottante renvoyant Infinity ou NaN - il s’agit de valeurs avec des significations spécifiques; il serait donc très déroutant que vous retourniez la valeur null ici.
  • La fonction est censée renvoyer une valeur unique , telle que findPerson (). Si elle a été conçue pour renvoyer une collection, par exemple findAllOldPeople (), une collection vide est préférable. Un corollaire à cela est qu'une fonction qui retourne une collection ne doit jamais renvoyer null .

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:

  • FindPerson - il serait approprié que cette fonction renvoie la valeur null si la personne n'a pas été trouvée
  • ReverseString - il semble qu'il ne manquerait jamais de trouver un résultat si une chaîne était transmise (puisque toutes les chaînes peuvent être inversées, y compris la chaîne vide). Donc, il ne devrait jamais retourner null. Si quelque chose ne va pas (manque de mémoire?), Alors il devrait lancer une exception.

1

À 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

f : S x T1 x ... x Tn -> T

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.

Customer findCustomer(String customerCode)

pour rechercher un client dans la base de données d'application par son code. Dans cette méthode, je voudrais

  • Retourne un objet de la classe Customer si la requête aboutit,
  • Renvoie null si la requête ne trouve aucun client.
  • Lancer une exception s'il n'est pas possible de se connecter à la base de données.

Les vérifications supplémentaires pour null, par exemple

Customer customer = findCustomer("...");
if (customer != null && customer.getOrders() > 0)
{
    ...
}

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.


0

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.


0

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.


0

Ajoutant à ce que les gens ont déjà dit sur le Maybeconstructeur 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 renvoie null.

Ceci est mon problème principal à la fois avec le retour nullet le retour d'un objet null. Ils sont trompeurs. nullCe 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 recevrez Nothing. Comment est-ce meilleur que null? pas seulement pour le risque de NPE. Mais aussi parce que le type de Nothingn'est pas seulement Maybe. Il est Maybe 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 de Maybe Customertout 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.


0

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.


-1
  • 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.


-2

Retourner NULL est une conception terrible , 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
  • pensée informatique vs pensée objet
  • objets 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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.