N'utilisez pas non plus si vous pouvez l'éviter. Utilisez une fonction d'usine renvoyant un type d'option, un tuple ou un type de somme dédié. En d'autres termes, représentent une valeur potentiellement par défaut avec un type différent d'une valeur garantie par défaut.
D'abord, listons les desiderata puis réfléchissons à la façon dont nous pouvons l'exprimer dans quelques langages (C ++, OCaml, Python)
- attraper l'utilisation non désirée d'un objet par défaut au moment de la compilation.
- indiquez clairement si une valeur donnée est potentiellement par défaut ou non lors de la lecture du code.
- choisissez notre valeur par défaut sensible, le cas échéant, une fois par type . Ne choisissez certainement pas un défaut potentiellement différent sur chaque site d'appel .
- le rendre facile pour les outils d'analyse statique ou un humain avec
grep
pour rechercher des erreurs potentielles.
- Pour certaines applications , le programme doit continuer normalement s'il reçoit une valeur par défaut de manière inattendue. Pour d'autres applications , le programme doit s'arrêter immédiatement s'il reçoit une valeur par défaut, idéalement de manière informative.
Je pense que la tension entre le modèle d'objet nul et les pointeurs nuls vient de (5). Si nous pouvons détecter les erreurs assez tôt, cependant, (5) devient théorique.
Considérons cette langue par langue:
C ++
À mon avis, une classe C ++ devrait généralement être constructible par défaut car elle facilite les interactions avec les bibliothèques et facilite l'utilisation de la classe dans les conteneurs. Cela simplifie également l'héritage car vous n'avez pas besoin de penser au constructeur de superclasse à appeler.
Cependant, cela signifie que vous ne pouvez pas savoir avec certitude si une valeur de type MyClass
est dans "l'état par défaut" ou non. En plus de mettre un bool nonempty
champ ou un champ similaire pour faire apparaître la valeur par défaut lors de l'exécution, le mieux que vous puissiez faire est de produire de nouvelles instances de MyClass
manière à obliger l'utilisateur à le vérifier .
Je recommanderais d'utiliser une fonction d'usine qui renvoie un std::optional<MyClass>
, std::pair<bool, MyClass>
ou une référence de valeur r à un std::unique_ptr<MyClass>
si possible.
Si vous voulez que votre fonction d'usine retourne une sorte d'état "d'espace réservé" qui est différent d'un construit par défaut MyClass
, utilisez un std::pair
et assurez-vous de documenter que votre fonction fait cela.
Si la fonction d'usine a un nom unique, il est facile de grep
rechercher le nom et de rechercher la négligence. Cependant, il est difficile de trouver grep
des cas où le programmeur aurait dû utiliser la fonction d'usine mais ne l'a pas fait .
OCaml
Si vous utilisez un langage comme OCaml, vous pouvez simplement utiliser un option
type (dans la bibliothèque standard OCaml) ou un either
type (pas dans la bibliothèque standard, mais facile à rouler le vôtre). Ou un defaultable
type (j'invente le terme defaultable
).
type 'a option =
| None
| Some of 'a
type ('a, 'b) either =
| Left of 'a
| Right of 'b
type 'a defaultable =
| Default of 'a
| Real of 'a
Un defaultable
comme indiqué ci-dessus est meilleur qu'une paire car l'utilisateur doit correspondre à un modèle pour extraire le 'a
et ne peut pas simplement ignorer le premier élément de la paire.
L'équivalent du defaultable
type indiqué ci-dessus peut être utilisé en C ++ en utilisant un std::variant
avec deux instances du même type, mais des parties de l' std::variant
API sont inutilisables si les deux types sont identiques. C'est aussi une utilisation étrange std::variant
car ses constructeurs de types sont sans nom.
Python
De toute façon, vous n'obtenez aucune vérification à la compilation pour Python. Mais, étant typé dynamiquement, il n'y a généralement pas de circonstances où vous avez besoin d'une instance d'espace réservé d'un certain type pour apaiser le compilateur.
Je recommanderais simplement de lever une exception lorsque vous seriez obligé de créer une instance par défaut.
Si ce n'est pas acceptable, je recommanderais de créer un DefaultedMyClass
qui hérite MyClass
et de le renvoyer de votre fonction d'usine. Cela vous offre de la flexibilité en termes de fonctionnalité de "stubbing out" dans les instances par défaut si vous en avez besoin.