Comme le notent les commentaires de @ benjamin-gruenbaum, cela s'appelle le piège booléen:
Dis que j'ai une fonction comme celle-ci
UpdateRow(var item, bool externalCall);
et dans mon contrôleur, cette valeur externalCall
sera toujours VRAIE. Quelle est la meilleure façon d'appeler cette fonction? J'écris d'habitude
UpdateRow(item, true);
Mais je me demande, devrais-je déclarer un booléen, juste pour indiquer ce que cette «vraie» valeur représente? Vous pouvez le savoir en regardant la déclaration de la fonction, mais c'est évidemment plus rapide et plus clair si vous venez de voir quelque chose comme
bool externalCall = true;
UpdateRow(item, externalCall);
PD: Je ne sais pas si cette question convient vraiment. Sinon, où pourrais-je obtenir plus d'informations à ce sujet?
PD2: Je n'ai tagué aucune langue parce que je pensais que c'était un problème très générique. Quoi qu'il en soit, je travaille avec c # et la réponse acceptée fonctionne pour c #
data CallType = ExternalCall | InternalCall
dans haskell par exemple.Réponses:
Il n'y a pas toujours de solution parfaite, mais vous avez le choix parmi plusieurs alternatives:
Utilisez des arguments nommés , s'ils sont disponibles dans votre langue. Cela fonctionne très bien et n'a pas d'inconvénients particuliers. Dans certaines langues, n'importe quel argument peut être passé sous forme d'argument nommé, par exemple
updateRow(item, externalCall: true)
( C # ) ouupdate_row(item, external_call=True)
(Python).Votre suggestion d'utiliser une variable distincte est un moyen de simuler des arguments nommés, mais ne présente pas les avantages en termes de sécurité associés (rien ne garantit que vous avez utilisé le nom de variable correct pour cet argument).
Utilisez des fonctions distinctes pour votre interface publique, avec de meilleurs noms. C'est une autre façon de simuler des paramètres nommés en plaçant les valeurs du paramètre dans le nom.
Ceci est très lisible, mais conduit à beaucoup de passe-passe pour vous, qui écrit ces fonctions. De plus, il ne peut pas gérer correctement une explosion combinatoire lorsqu'il existe plusieurs arguments booléens. Un inconvénient majeur est que les clients ne peuvent pas définir cette valeur de manière dynamique, mais doivent utiliser if / else pour appeler la fonction correcte.
Utilisez un enum . Le problème avec les booléens est qu'ils s'appellent "vrai" et "faux". Donc, introduisez plutôt un type avec de meilleurs noms (par exemple
enum CallType { INTERNAL, EXTERNAL }
). De plus, cela augmente la sécurité de type de votre programme (si votre langage implémente des énumérations en tant que types distincts). L'inconvénient des enums est qu'ils ajoutent un type à votre API visible publiquement. Cela n'a pas d'importance pour les fonctions purement internes et les enums ne présentent pas d'inconvénient majeur.Dans les langues sans énums, des chaînes courtes sont parfois utilisées à la place. Cela fonctionne et peut même être meilleur que les booléens crus, mais est très sensible aux fautes de frappe. La fonction devrait alors immédiatement affirmer que l'argument correspond à un ensemble de valeurs possibles.
Aucune de ces solutions n’a un impact négatif sur les performances. Les paramètres nommés et les énumérations peuvent être résolus complètement lors de la compilation (pour un langage compilé). L'utilisation de chaînes peut impliquer une comparaison de chaînes, mais son coût est négligeable pour les littéraux de chaîne de petite taille et la plupart des types d'applications.
la source
La bonne solution est de faire ce que vous suggérez, mais de le regrouper dans une mini-façade:
La lisibilité l'emporte sur la micro-optimisation. Vous pouvez vous permettre l’appel de fonction supplémentaire, bien mieux que l’effort du développeur qui doit rechercher la sémantique du drapeau booléen, même une fois.
la source
true
fait.) coûte autant de temps processeur que de gain de temps pour le développeur, car le temps processeur est beaucoup moins cher.UpdateRow(item, true /*external call*/);
serait plus propre, dans des langues qui permettent cette syntaxe de commentaire. Gonfler votre code avec une fonction supplémentaire juste pour éviter d'écrire un commentaire ne semble pas en valoir la peine pour un cas simple. Peut-être que s'il y avait beaucoup d'autres arguments pour cette fonction, et / ou un code environnant encombré + délicat, cela commencerait à faire plus appel. Mais je pense que si vous déboguez, vous finirez par vérifier la fonction wrapper pour voir ce qu'elle fait et devez vous rappeler quelles fonctions sont des wrappers minces autour d'une API de bibliothèque et qui ont en réalité une certaine logique.Pourquoi avez-vous une fonction comme celle-ci?
Dans quelles circonstances l' appelleriez-vous avec l'argument externalCall défini sur des valeurs différentes?
Si l’un provient d’une application cliente externe, par exemple, et que l’autre fait partie du même programme (c’est-à-dire de modules de code différents), je dirais alors que vous devriez avoir deux méthodes différentes , une pour chaque cas, éventuellement même définie sur Interfaces
Si, toutefois, vous passez l'appel en vous basant sur une donnée provenant d'une source autre que le programme (par exemple, un fichier de configuration ou une base de données lue), la méthode de passage booléen serait plus logique.
la source
Bien que je convienne qu'il est idéal d'utiliser une fonctionnalité de langage pour renforcer la lisibilité et la sécurité des valeurs, vous pouvez également opter pour une approche pratique : les commentaires à l'heure de l'appel. Comme:
ou:
ou (à juste titre, comme suggéré par Frax):
la source
UpdateRow(item, /* externalCall */ true )
. Le commentaire de phrase complète est beaucoup plus difficile à analyser, il s’agit essentiellement de bruit (en particulier la deuxième variante, qui est également associée de manière très vague à l’argument).Vous pouvez 'nommer' vos bools. Vous trouverez ci-dessous un exemple de langage OO (où il peut être exprimé dans la classe fournissant
UpdateRow()
), mais le concept lui-même peut être appliqué dans n’importe quel langage:et sur le site de l'appel:
Je pense que le premier élément est meilleur, mais il ne force pas l'utilisateur à utiliser de nouvelles variables lorsqu'il utilise la fonction. Si la sécurité du type est importante pour vous, vous pouvez créer un
enum
type et le faireUpdateRow()
accepter à la place d'unbool
:UpdateRow(var item, ExternalCallAvailability ec);
Vous pouvez modifier le nom de la fonction pour qu’elle reflète
bool
mieux la signification du paramètre. Pas très sûr mais peut-être:UpdateRowWithExternalCall(var item, bool externalCall)
la source
externalCall=false
, son nom n'a aucun sens. Le reste de votre réponse est bon.UpdateRowWithUsuallyExternalButPossiblyInternalCall
.Une autre option que je n'ai pas encore lue est la suivante: Utiliser un IDE moderne.
Par exemple, IntelliJ IDEA imprime le nom de variable des variables dans la méthode que vous appelez si vous passez un littéral tel que
true
ounull
ouusername + “@company.com
. Ceci est fait dans une petite police pour ne pas prendre trop de place sur votre écran et avoir un aspect très différent du code réel.Je ne dis toujours pas que c'est une bonne idée de jeter des booléens partout. L'argument selon lequel vous lisez le code beaucoup plus souvent que vous écrivez est souvent très fort, mais dans ce cas particulier, cela dépend énormément de la technologie que vous (et vos collègues!) Utilisez pour soutenir votre travail. Avec un IDE, le problème est beaucoup moins grave qu'avec, par exemple, vim.
Exemple modifié d'une partie de ma configuration de test:
la source
2 jours et aucun n'a mentionné le polymorphisme?
Lorsque je suis un client souhaitant mettre à jour une ligne avec un élément, je ne veux pas penser au nom de la base de données, à l'URL du micro-service, au protocole utilisé pour communiquer ou au fait qu'un appel externe soit ou non devra être utilisé pour y arriver. Arrête de pousser ces détails sur moi. Prenez juste mon article et allez déjà mettre à jour une ligne quelque part.
Faites cela et cela devient une partie du problème de construction. Cela peut être résolu de plusieurs façons. En voici un:
Si vous regardez cela et que vous pensez "Mais mon langage a des arguments nommés, je n'ai pas besoin de cela". Bien, super, allez les utiliser. Gardez cette absurdité à l’abri du client appelant qui ne devrait même pas savoir à quoi il parle.
Il convient de noter que ceci est plus qu'un problème sémantique. C'est aussi un problème de design. Passez un booléen et vous êtes obligé d'utiliser des branches pour le gérer. Pas très orienté objet. Avez-vous deux booléens ou plus à passer? Souhaitant que votre langue ait plusieurs envois? Examinez ce que les collections peuvent faire lorsque vous les imbriquez. La bonne structure de données peut vous faciliter la vie.
la source
Si vous pouvez modifier la
updateRow
fonction, vous pouvez peut-être la reformuler en deux fonctions. Étant donné que la fonction prend un paramètre booléen, je suppose que cela ressemble à ceci:Cela peut être un peu une odeur de code. La fonction peut avoir un comportement radicalement différent en fonction de la
externalCall
variable, auquel cas elle a deux responsabilités différentes. Le refactoriser en deux fonctions qui n'ont qu'une seule responsabilité peut améliorer la lisibilité:Désormais, partout où vous appelez ces fonctions, vous pouvez savoir en un coup d’œil si le service externe est appelé.
Ce n'est pas toujours le cas, bien sûr. C'est la situation et une question de goût. Refactoriser une fonction qui prend un paramètre booléen en deux fonctions est généralement intéressant, mais ce n’est pas toujours le meilleur choix.
la source
Si UpdateRow est dans une base de code contrôlée, je considérerais un modèle de stratégie:
Où Request représente une sorte de DAO (un rappel dans un cas trivial).
Vrai cas:
Faux cas:
Habituellement, l'implémentation du point de terminaison est configurée à un niveau d'abstraction plus élevé et je l'utilise simplement ici. En tant qu'effet secondaire utile, cela me donne la possibilité d'implémenter un autre moyen d'accéder aux données distantes, sans aucune modification du code:
En général, une telle inversion de contrôle garantit un couplage moindre entre votre stockage de données et votre modèle.
la source