Par exemple, supposons que j'ai une classe Member
, qui a un lastChangePasswordTime:
class Member{
.
.
.
constructor(){
this.lastChangePasswordTime=null,
}
}
dont lastChangePasswordTime peut être absent de manière significative, car certains membres peuvent ne jamais changer leurs mots de passe.
Mais selon Si les valeurs nulles sont mauvaises, que faut-il utiliser lorsqu'une valeur peut être absente de manière significative? et https://softwareengineering.stackexchange.com/a/12836/248528 , je ne devrais pas utiliser null pour représenter une valeur significativement absente. Alors j'essaie d'ajouter un drapeau booléen:
class Member{
.
.
.
constructor(){
this.isPasswordChanged=false,
this.lastChangePasswordTime=null,
}
}
Mais je pense que c'est assez obsolète parce que:
Lorsque isPasswordChanged a la valeur false, lastChangePasswordTime doit être null et la vérification de lastChangePasswordTime == null est presque identique à la vérification que isPasswordChanged est false, je préfère donc vérifier lastChangePasswordTime == null directement
En changeant la logique ici, je peux oublier de mettre à jour les deux champs.
Remarque: lorsqu'un utilisateur change de mot de passe, j'enregistre l'heure comme ceci:
this.lastChangePasswordTime=Date.now();
Le champ booléen supplémentaire est-il meilleur qu'une référence nulle ici?
std::optional
ouOption
. Dans d’autres langues, vous devrez peut-être créer vous-même un mécanisme approprié, ou bien vous pourrez recourir ànull
quelque chose de similaire, car il est plus idiomatique.lastChangePasswordTime
peut être un pointeur non initialisé ici, et le comparer à quoi que ce soit serait un comportement indéfini. Ce n'est pas une raison vraiment convaincante de ne pas initialiser le pointeur surNULL
/ à lanullptr
place, surtout pas en C ++ moderne (où vous n'utiliseriez pas de pointeur du tout), mais qui sait? Un autre exemple serait les langues sans pointeurs, ou avec un mauvais support pour les pointeurs, peut-être. (FORTRAN 77 me vient à l'esprit ...)Réponses:
Je ne vois pas pourquoi, si vous avez une valeur absente de manière significative,
null
ne devrait pas être utilisé si vous êtes délibéré et prudent à ce sujet.Si votre objectif est d’entourer la valeur nullable afin d’éviter de la référencer accidentellement, nous vous suggérons de créer la
isPasswordChanged
valeur en tant que fonction ou propriété renvoyant le résultat d’un contrôle nul, par exemple:À mon avis, le faire de cette façon:
isPasswordChanged
Cela vous évite d'avoir à vous soucier de maintenir la valeur que vous mentionnez.La façon dont vous conservez les données (probablement dans une base de données) serait responsable de la préservation des valeurs NULL.
la source
isPasswordChanged
tout comme vous pouvez oublier de vérifier si la valeur est nulle. Je ne vois rien gagné ici.nulls
ne sont pas mauvais. Les utiliser sans réfléchir est. C'est un cas oùnull
la réponse est exacte - il n'y a pas de date.Notez que votre solution crée plus de problèmes. Quel est le sens de la date étant mise à quelque chose, mais le
isPasswordChanged
est faux? Vous venez de créer un cas d'informations conflictuelles que vous devez capturer et traiter spécialement, alors qu'unenull
valeur a un sens clairement défini et non ambigu et ne peut pas être en conflit avec d'autres informations.Alors non, votre solution n'est pas meilleure. Permettre une
null
valeur est la bonne approche ici. Les gens qui prétendent quenull
c'est toujours mauvais, peu importe le contexte, ne comprennent pas pourquoinull
existe.la source
null
existe parce que Tony Hoare a commis l’erreur de 1 milliard de dollars en 1965. Les gens qui prétendent que celanull
est "pervers" simplifient excessivement le sujet, bien sûr, mais c’est une bonne chose que les langages modernes ou les styles de programmation changent peu à peunull
. il avec des types d'option dédiés.DateTime
champ est un pointeur. Le concept entier denull
n'a de sens que pour les pointeurs!Selon votre langage de programmation, il peut exister de bonnes alternatives, telles qu'un
optional
type de données. En C ++ (17), ce serait std :: optional . Il peut être absent ou avoir une valeur arbitraire du type de données sous-jacent.la source
Optional
en java; le sens d'un nulOptional
étant à vous ...Optional<Date> lastPasswordChangeTime = null;
, mais je n'abandonnerais pas à cause de cela. Au lieu de cela, je voudrais instiller une règle d'équipe très fermement, qu'aucune valeur optionnelle ne peut jamais être assignéenull
. En option, vous achetez trop de fonctionnalités intéressantes pour pouvoir y renoncer facilement. Vous pouvez facilement attribuer des valeurs par défaut (orElse
ou avec une évaluation paresseuse:)orElseGet
, des erreurs de projection (orElseThrow
), transformer des valeurs de manière sécurisée (map
,flatmap
), etc.isPresent
est presque toujours une odeur de code, selon mon expérience, et un signe assez fiable que les développeurs qui utilisent ces options sont "manquants". En effet, cela n’apporte fondamentalement aucun avantageref == null
, mais ce n’est pas quelque chose que vous devriez utiliser souvent. Même si l'équipe est instable, un outil d'analyse statique peut énoncer la loi, comme un linter ou un FindBugs , qui peut intercepter la plupart des cas.null
parce que la langue est 100% compatible avec Java, mais personne ne l' utilise, etOption[T]
est partout, avec un excellent support fonctionnel programmation commemap
,flatMap
, correspondance de motif et ainsi de suite, qui va loin, bien au - delà des== null
vérifications. Vous avez raison de dire qu'en théorie, un type d'option ne ressemble guère à un pointeur glorifié, mais la réalité prouve que cet argument est faux.Utiliser correctement null
Il y a différentes façons d'utiliser
null
. La manière la plus courante et sémantiquement correcte consiste à l'utiliser lorsque vous pouvez avoir ou non une valeur unique . Dans ce cas, une valeur est égalenull
ou significative (par exemple, un enregistrement de base de données).Dans ces situations, vous l'utilisez principalement comme ceci (en pseudo-code):
Problème
Et c'est un très gros problème. Le problème est qu’au moment où vous appelez,
doSomethingUseful
la valeur n’a peut-être pas été vérifiéenull
! Si ce n'était pas le cas, le programme va probablement planter. Et l'utilisateur peut même ne pas voir de bons messages d'erreur, laissant quelque chose comme "une erreur horrible: valeur recherchée mais nulle"! " (après la mise à jour: bien qu'il puisse y avoir encore moins d'erreur informative commeSegmentation fault. Core dumped.
, ou pire encore, aucune erreur et une manipulation incorrecte sur null dans certains cas)Oublier d'écrire des chèques
null
et de gérer desnull
situations est un bogue extrêmement courant . Voilà pourquoi Tony Hoare qui a inventénull
lors d' une conférence de logiciel appelé QCon Londres en 2009 qu'en 1965 , il a fait l'erreur d'un milliard de dollars: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar- Erreur-Tony-HoareÉviter le problème
Certaines technologies et langues rendent la vérification
null
impossible à oublier de différentes manières, réduisant ainsi le nombre de bugs.Par exemple, Haskell a la
Maybe
monade au lieu de NULL. Supposons qu'il s'agisse d'DatabaseRecord
un type défini par l'utilisateur. Dans Haskell, une valeur de typeMaybe DatabaseRecord
peut être égaleJust <somevalue>
ou égale àNothing
. Vous pouvez ensuite l'utiliser de différentes manières, mais vous ne pouvez pas appliquer une opérationNothing
sans le savoir, peu importe la façon dont vous l'utilisez .Par exemple, cette fonction appelée
zeroAsDefault
renvoiex
pourJust x
et0
pourNothing
:Christian Hackl dit que C ++ 17 et Scala ont leurs propres méthodes. Donc, vous voudrez peut-être essayer de savoir si votre langue a quelque chose comme ça et l'utiliser.
Les null sont encore largement utilisés
Si vous n'avez rien de mieux à utiliser, alors
null
c'est bien. Continuez juste à le surveiller. Les déclarations de type dans les fonctions vous aideront quand même un peu.En outre, cela peut sembler peu progressif, mais vous devriez vérifier si vos collègues veulent utiliser
null
quelque chose d’autre. Ils peuvent être conservateurs et ne pas vouloir utiliser de nouvelles structures de données pour certaines raisons. Par exemple, supporter les anciennes versions d'une langue. Ces éléments doivent être déclarés dans les normes de codage du projet et dûment discutés avec l'équipe.Sur votre proposition
Vous suggérez d'utiliser un champ booléen séparé. Mais vous devez quand même le vérifier et vous pouvez toujours oublier de le vérifier. Donc, il n'y a rien gagné ici. Si vous pouvez même oublier autre chose, comme mettre à jour les deux valeurs à chaque fois, c'est encore pire. Si le problème de l’oubli de la vérification
null
n’est pas résolu, cela ne sert à rien. Éviternull
est difficile et vous ne devriez pas le faire de manière à aggraver les choses.Comment ne pas utiliser null
Enfin, il existe des moyens courants d'utiliser de
null
manière incorrecte. L’un de ces moyens consiste à l’utiliser à la place de structures de données vides telles que des tableaux et des chaînes. Un tableau vide est un tableau approprié comme un autre! Il est presque toujours important et utile que les structures de données, qui peuvent contenir plusieurs valeurs, puissent être vides, c'est-à-dire qu'elles ont une longueur égale à 0.Du point de vue de l'algèbre, une chaîne vide pour les chaînes ressemble beaucoup à 0 pour les nombres, c'est-à-dire l'identité:
Une chaîne vide permet aux chaînes de devenir en général un monoïde: https://en.wikipedia.org/wiki/Monoid Si vous ne l'obtenez pas, ce n'est pas si important pour vous.
Voyons maintenant pourquoi il est important de programmer avec cet exemple:
Si nous passons ici un tableau vide, le code fonctionnera correctement. Cela ne fera que rien. Cependant, si nous passons un
null
ici, nous aurons probablement un crash avec une erreur du type "impossible de parcourir en boucle null, désolé". Nous pourrions l'envelopperif
mais c'est moins propre et encore une fois, vous pourriez oublier de vérifierComment gérer null
Ce qui
doSomethingAboutIt()
devrait être fait et surtout s’il faut lever une exception est une autre question compliquée. En bref, cela dépend de la question de savoir sinull
était une valeur d'entrée acceptable pour une tâche donnée et ce qui est attendu en réponse. Les exceptions concernent des événements inattendus. Je n'irai pas plus loin dans ce sujet. Cette réponse est déjà très longue.la source
horrible error: wanted value but got null!
c'est beaucoup mieux que le plus typiqueSegmentation fault. Core dumped.
...Error: the product you want to buy is out of stock
.null
oùnull
n'est pas autorisé est une erreur de programmation, c'est-à-dire un bug dans le code. Un produit en rupture de stock fait partie de la logique métier ordinaire et n'est pas un bug. Vous ne pouvez pas et ne devez pas essayer de traduire la détection d’un bogue en un message normal dans l’interface utilisateur. Il est préférable d’intervenir immédiatement et de ne pas exécuter plus de code, surtout s’il s’agit d’une application importante (par exemple, une application qui effectue de véritables transactions financières).null
entièrement, même de l'arrière-plan.En plus de toutes les très bonnes réponses données précédemment, j'ajouterais que chaque fois que vous êtes tenté de laisser un champ nul, réfléchissez bien s'il peut s'agir d'un type de liste. Un type nullable est équivalent à une liste de 0 ou 1 éléments, ce qui peut souvent être généralisé à une liste de N éléments. Plus précisément dans ce cas, vous pouvez envisager de
lastChangePasswordTime
constituer une liste depasswordChangeTimes
.la source
Posez-vous la question suivante: quel comportement requiert le champ lastChangePasswordTime?
Si vous avez besoin de ce champ pour une méthode IsPasswordExpired () afin de déterminer si un membre doit être invité à changer son mot de passe de temps en temps, je définirais le champ à la date à laquelle le membre a été créé. L'implémentation IsPasswordExpired () est la même pour les membres nouveaux et existants.
Si vous avez une exigence distincte que les membres nouvellement créés doivent mettre à jour leur mot de passe, je voudrais ajouter un champ booléen appelé séparés passwordShouldBeChanged et à true lors de la création. Je voudrais ensuite changer les fonctionnalités de la méthode IsPasswordExpired () pour inclure une vérification de ce champ (et renommer la méthode en ShouldChangePassword).
Rendez vos intentions explicites dans le code.
la source
Tout d’abord, le fait que le mal soit nul est un dogme et, comme d’habitude avec un dogme, il fonctionne mieux comme ligne directrice que comme test de réussite ou non.
Deuxièmement, vous pouvez redéfinir votre situation de manière à ce que la valeur ne puisse jamais être nulle. InititialPasswordChanged est un booléen initialement défini sur false, PasswordSetTime est la date et l'heure auxquelles le mot de passe actuel a été défini.
Notez que bien que cela vienne à un léger coût, vous pouvez maintenant TOUJOURS calculer le temps écoulé depuis la dernière définition d’un mot de passe.
la source
Les deux sont «sûrs / sains / corrects» si l'appelant vérifie avant l'utilisation. Le problème est ce qui se passe si l'appelant ne vérifie pas. Quel est le meilleur, une sorte d'erreur de null ou d'utiliser une valeur invalide?
Il n'y a pas une seule bonne réponse. Cela dépend de ce qui vous inquiète.
Si les plantages sont vraiment graves mais que la réponse n'est pas critique ou a une valeur par défaut acceptée, il est peut-être préférable d'utiliser un booléen comme indicateur. Si utiliser la mauvaise réponse est un problème pire que de planter, utiliser null est préférable.
Pour la majorité des cas "typiques", un échec rapide et le fait de forcer les appelants à vérifier est le moyen le plus rapide d'obtenir du code sain d'esprit, et par conséquent, je pense que null devrait être le choix par défaut. Je ne mettrais pas trop de foi dans le "X est la racine de tout le mal" évangélistes cependant; ils n'ont normalement pas anticipé tous les cas d'utilisation.
la source