Définir une variable pour nommer un argument de méthode est-il une bonne pratique?

73

Par souci de lisibilité, je suis souvent amené à définir des variables temporaires lors de l'appel de fonctions, telles que le code suivant.

var preventUndo = true;
doSomething(preventUndo);

La version plus courte de ceci à ceci serait,

doSomething(true);

Mais quand je reviens au code, je me demande souvent à quoi on se trueréfère. Existe-t-il une convention pour ce genre d’énigme?

duopixel
la source
5
Utilisez-vous une sorte d'IDE? La plupart auront une sorte de moyen d'indiquer quels sont les paramètres de la fonction que vous appelez, ce qui annule quelque peu la nécessité de le faire.
Matthew Scharley
4
Je n'utilise pas d'IDE, mais même dans ce cas, je suppose que la suggestion nécessiterait une action (passer la souris ou se déplacer sur la ligne). J'aime savoir ce que fait mon code simplement en le regardant.
methodofaction
11
Si le langage le supporte, vous pouvez utiliser une énumération telle quedoSomething( Undo.PREVENT )
James P.
4
Mais vous pouvez définir Undo = { PREVENT = true, DONT_PREVENT = false }. Mais en JavaScript, la convention est de le faire comme ça: function myFunction( mandatoryArg1, mandatoryArg2, otherArgs ) { /*...*/ }et ensuite myFunction( 1, 2, { option1: true, option2: false } ).
xavierm02
6
Cela dépend de la langue. S'il s'agissait de python, par exemple, je suggérerais simplement d'utiliser des arguments de mots clés, par exempledoSomething(preventUndo=True)
Joel Cornett

Réponses:

117

Explication des variables

Votre cas est un exemple d' introduction expliquant le refactoring des variables . En bref, une variable explicative est une variable qui n'est pas strictement nécessaire, mais permet de donner un nom clair à quelque chose, dans le but d'améliorer la lisibilité.

Un code de bonne qualité communique l’ intention du lecteur; et en tant que développeur professionnel, la lisibilité et la maintenabilité sont vos objectifs n ° 1.

En tant que telle, la règle empirique que je recommanderais est la suivante: si le but de votre paramètre n’est pas immédiatement évident, n'hésitez pas à utiliser une variable pour lui donner un bon nom. Je pense que c'est une bonne pratique en général (à moins d'abus). Voici un exemple rapide et artificiel - considérez:

editButton.Enabled = (_grid.SelectedRow != null && ((Person)_grid.SelectedRow).Status == PersonStatus.Active);

par rapport au légèrement plus long, mais peut-être plus clair:

bool personIsSelected = (_grid.SelectedRow != null);
bool selectedPersonIsEditable = (personIsSelected && ((Person)_grid.SelectedRow).Status == PersonStatus.Active)
editButton.Enabled = (personIsSelected && selectedPersonIsEditable);

Paramètres booléens

Votre exemple montre en réalité pourquoi les booléens dans les API sont souvent une mauvaise idée . Du côté des appels, ils ne font rien pour expliquer ce qui se passe. Considérer:

ParseFolder(true, false);

Vous devez rechercher la signification de ces paramètres. s'ils étaient des énums, ce serait beaucoup plus clair:

ParseFolder(ParseBehaviour.Recursive, CompatibilityOption.Strict);

Modifier:

Ajout de titres et échange de l'ordre des deux paragraphes principaux, car trop de personnes se concentraient sur la partie relative aux paramètres booléens (pour être juste, il s'agissait du premier paragraphe à l'origine). Également ajouté un exemple à la première partie.

Daniel B
la source
28
Sauf si vous utilisez des paramètres nommés (arguments nommés dans .NET Framework), dans ce cas, cela doSomething(preventUndo: true);est suffisamment clair. En outre, créer une énumération pour chaque booléen de votre API? Sérieusement?
Arseni Mourzenko
12
@MainMa: Si la langue que vous utilisez n'est pas assez cool pour supporter les arguments nommés, utiliser un enum est une bonne option. Cela ne signifie pas que vous devriez systématiquement introduire des énumérations partout.
Giorgio
18
@MainMa - Pour répondre à cette question, le fait est que vous ne devriez pas avoir de booléens dans vos API (du moins vos publiques). Vous devez passer à une syntaxe d'argument nommé, qui n'est sans doute pas idiomatique (au moment de l'écriture), et dans tous les cas, un paramètre booléen signifie presque toujours que votre méthode fait deux choses et que vous autorisez l'appelant à choisir. Mais le but de ma réponse était qu'il n'y avait rien de mal à utiliser des variables explicatives dans votre code; c'est en fait une très bonne pratique.
Daniel B
3
C'est une partie permanente du langage C #, de quoi a-t-elle besoin de plus "d'acceptation générale"? Même pour quelqu'un qui n'est pas déjà au courant de la construction (ce qui signifie que vos développeurs C # n'ont même pas lu la documentation C #), le résultat est assez évident.
Nate CK
3
C’est la raison pour laquelle MS publie ces résumés de tous les nouveaux éléments qu’ils ont ajoutés dans chaque version. Je ne pense pas que la lecture de l’un de ces éléments tous les deux ans crée beaucoup de travail: msdn.microsoft.com/fr us / library / bb383815% 28v = vs.100% 29.aspx
Nate CK
43

N'écris pas le code dont tu n'as pas besoin.

Si vous avez du doSomething(true)mal à comprendre, vous devez soit ajouter un commentaire:

// Do something and prevent the undo.
doSomething(true);

ou, si la langue le permet, ajoutez le nom du paramètre:

doSomething(preventUndo: true);

Sinon, comptez sur votre IDE pour vous donner la signature de la méthode appelée:

entrez la description de l'image ici

Cas utiles

Mettre une variable supplémentaire peut être utile:

  1. Pour le débogage:

    var productId = this.Data.GetLastProductId();
    this.Data.AddToCart(productId);
    

    Le même code peut être écrit sur une seule ligne, mais si vous voulez placer un point d'arrêt avant d'ajouter un produit au panier afin de voir si l'ID de produit est correct, écrire deux lignes au lieu d'une seule est une bonne idée.

  2. Si vous avez une méthode avec beaucoup de paramètres et que chaque paramètre passe d’une évaluation. Dans une ligne, cela peut devenir totalement illisible.

    // Even with indentation, this is unreadable.
    var doSomething(
        isSomethingElse ? 0 : this.getAValue(),
        this.getAnotherOne() ?? this.default,
        (a + b + c + d + e) * f,
        this.hello ? this.world : (this.hello2 ? this.world2 : -1));
    
  3. Si l'évaluation d'un paramètre est trop compliquée. Un exemple de code que j'ai vu:

    // Wouldn't it be easier to have several if/else's (maybe even in a separate method)?
    do(something ? (hello ? world : -1) : (programmers ? stackexchange : (com ? -1 : 0)));
    

Pourquoi pas dans d'autres cas?

Pourquoi ne devriez-vous pas créer de variables supplémentaires dans des cas simples?

  1. Pas à cause de l'impact sur les performances. Ce serait une très mauvaise hypothèse de la part d'un développeur débutant qui optimise son application. Il n'y a pas d'impact sur les performances dans la plupart des langages, car le compilateur insère la variable en ligne. Dans les langages où le compilateur ne le fait pas, vous pouvez gagner quelques microsecondes en l’alignant manuellement, ce qui ne vaut pas la peine. Ne fais pas ça.

  2. Mais à cause du risque de dissocier le nom que vous donnez à la variable au nom du paramètre.

    Exemple:

    Disons que le code original est:

    void doSomething(bool preventUndo)
    {
        // Does something very interesting.
        undoHistory.removeLast();
    }
    
    // Later in code:
    var preventUndo = true;
    doSomething(preventUndo);
    

    Plus tard, un développeur travaillant sur des doSomethingnotifications qui récemment, l’historique des annulations, a introduit deux nouvelles méthodes:

    undoHistory.clearAll() { ... }
    undoHistory.disable() { ... }
    

    Maintenant, preventUndosemble pas très clair. Cela signifie-t-il que seule la dernière action sera empêchée? Ou peut-être que cela signifie que l'utilisateur ne pourra plus utiliser la fonction d'annulation? Ou que l'historique d'annulation sera effacé? Les noms les plus clairs seraient:

    doSomething(bool hideLastUndo) { ... }
    doSomething(bool removeAllUndo) { ... }
    doSomething(bool disableUndoFeature) { ... }
    

    Alors maintenant vous avez:

    void doSomething(bool hideLastUndo)
    {
        // Does something very interesting.
        undoHistory.removeLast();
    }
    
    // Later in code, very error prone, while the signature of the method is clear:
    var preventUndo = true;
    doSomething(preventUndo);
    

Qu'en est-il des enums?

D'autres personnes ont suggéré d'utiliser des enums. Bien que cela résolve le problème immédiat, il en crée un plus gros. Nous allons jeter un coup d'oeil:

enum undoPrevention
{
    keepInHistory,
    prevent,
}

void doSomething(undoPrevention preventUndo)
{
    doTheJob();
    if (preventUndo == undoPrevention.prevent)
    {
        this.undoHistory.discardLastEntry();
    }
}

doSomething(undoPrevention.prevent);

Des problèmes avec ça:

  1. C'est trop de code. if (preventUndo == undoPrevention.prevent)? Sérieusement?! Je ne veux pas écrire de tels ifs à chaque fois.

  2. L'ajout d'un élément à l'énumération est très tentant plus tard, si j'utilise la même enum ailleurs. Et si je le modifie comme ceci:

    enum undoPrevention
    {
        keepInHistory,
        prevent,
        keepButDisable, // Keeps the entry in the history, but makes it disabled.
    }
    

    Ce qui va se passer maintenant? La doSomethingméthode fonctionnera-t-elle comme prévu? Pour éviter cela, il faudrait écrire cette méthode depuis le début:

    void doSomething(undoPrevention preventUndo)
    {
        if (![undoPrevention.keepInHistory, undoPrevention.prevent].contains(preventUndo))
        {
            throw new ArgumentException('preventUndo');
        }
    
        doTheJob();
        if (preventUndo == undoPrevention.prevent)
        {
            this.undoHistory.discardLastEntry();
        }
    }
    

    La variante qui utilise des booléens commence à être si belle!

    void doSomething(bool preventUndo)
    {
        doTheJob();
        if (preventUndo)
        {
            this.undoHistory.discardLastEntry();
        }
    }
    
MainMa
la source
3
"C’est trop de code. If (preventUndo == undoPrevention.prevent)? Sérieusement?! Je ne veux pas écrire de tels ifs à chaque fois.": Qu’en est-il de l’utilisation d’une bonne vieille instruction switch? De plus, si vous utilisez un booléen, vous avez quand même besoin d'une instruction if. Honnêtement, vos critiques sur ce point me semblent un peu artificielles.
Giorgio
1
Il me manque une solution alternative ici. Etes-vous en train de dire qu'il devrait s'en tenir au dicton-rien purement vrai et faux et s'en remettre à l'IDE (qu'il n'utilise pas)? Un commentaire pourrait pourrir comme le nom de la variable.
sécurité
2
@superM: Je ne trouve pas beaucoup plus difficile de changer le nom d'une variable temporaire utilisée uniquement pour l'appel. Si un nom d'énumération est modifié, c'est encore mieux, car le code d'appel ne fonctionnera plus et vous affichera une erreur. Si la fonctionnalité de la fonction est radicalement modifiée par une extension enum, vous devez alors réexaminer tous les appels pour plus de précision, sinon, vous programmez sur espoir, pas sur logique. Pas même de penser à changer les valeurs, par exemple la négation preventUndoà allowUndola signature ...
Sécurisez
4
@superM synchroniser les commentaires avec le code est en effet très problématique, car votre compilateur ne vous aide pas dans votre recherche d'incohérences.
Buhb
7
"... comptez sur votre IDE pour vous donner la signature de la méthode appelée" - cela est utile lorsque vous écrivez le code, mais pas autant lorsque vous le lisez. Lorsque je lis du code pour une classe, je veux savoir ce qu’il fait en le regardant. Si vous devez compter sur l'EDI pour la lisibilité, votre code n'est pas auto-documenté.
Phil
20

Ni l'un ni l'autre, vous ne devriez pas écrire de méthodes avec des drapeaux booléens.

Selon Robert C. Martin, dans son livre Clean Code (ISBN-13 978-0-13-235088-4), "Transférer un booléen dans une fonction est une pratique vraiment terrible."

Pour le paraphraser, le raisonnement est que le fait que vous ayez un commutateur true / false signifie que votre méthode fait probablement deux choses différentes (c'est-à-dire "faire quelque chose avec undo" et "faire quelque chose sans undo"), et devrait donc être diviser en deux méthodes différentes (qui peuvent appeler la même chose en interne).

DoSomething()
{...

DoSomethingUndoable()
{....
Nick B.
la source
7
Selon cette logique, mon seul constructeur HotDog devrait comporter environ 16 méthodes différentes: HotDogWithMustardRelishKrautOnion (), HotDogWithMustardNorelishKrautOnion (), etc. Ou bien, cela pourrait être un simple HotDogWithOptions (options int), dans lequel j'interprète les options comme un champ de bits, mais nous revenons à une méthode qui effectue plusieurs choses prétendument différentes. Donc, si je choisis la première option, est-ce que je dois écrire un gros conditionnel pour déterminer quel constructeur appeler en fonction des paramètres qui auraient autrement été des paramètres booléens? Et si le Q utilise un int, ce A ne répond pas au Q.
Caleb
4
Après avoir lu toutes les réponses et examiné mon code, je conclus que c’est la bonne façon de procéder. doSomethingUndoable()pourrait capturer les changements et ensuite appelerdoSomething()
methodofaction
1
Donc, si j'ai une fonction qui traite beaucoup de choses et a l'option d'envoyer un email quand c'est fait, je ne devrais pas avoir d'argument booléen pouvant empêcher l'envoi de l'email, je devrais créer une fonction séparée?
mardi
2
@Caleb Dans ce cas, je ferais un hot-dog et ajouterais les ingrédients un par un ou, dans un langage fortement typé , je passerais un objet HotDogSettings dans lequel j'aurais placé tous les drapeaux booléens. Je n'aurais pas 15 différents drapeaux vrai / faux qui seraient un cauchemar à entretenir. Rappelez-vous que le code concerne la maintenance et non l'écriture car 80% du coût de l'application est. var hd = MakeHotDog (); hd.Add (Onion); ...
Nick B.
2
@Caleb Je pense que la distinction est que votre exemple utilise les booléens comme un champ de données réel, alors que la plupart des utilisations de celui-ci servent à diriger le flux d'exécution; c'est pourquoi l'oncle Bob a raillé contre elle. En outre, son conseil est un peu extrême - les prochaines lignes du livre déconseillent de définir plus de 2 paramètres, ce qui est peut-être encore plus extrême. Il y a une méthode à la folie cependant. Vous avez absolument raison de dire que cela ne répond pas à la question, une erreur que j'ai commise dans ma propre réponse.
Daniel B
5

Quand vous regardez deux classes pour voir comment Couplé ils sont, l' une des catégories est un couplage de données , qui fait référence au code dans une classe appelant des méthodes d' une autre classe et juste de passage des données, comme ce mois , vous voulez un rapport et un autre catégorie est le couplage de contrôle , l’appel de méthodes et la transmission de quelque chose qui contrôle le comportement de la méthode. L'exemple que j'utilise en classe est un verbosedrapeau ou un reportTypedrapeau, mais preventUndoc'est aussi un excellent exemple. Comme vous venez de le démontrer, le couplage de contrôle rend difficile la lecture du code d'appel et la compréhension de ce qui se passe. Cela rend également le code appelant vulnérable aux modifications doSomething()qui utilisent le même indicateur pour contrôler à la fois l'annulation et l'archivage, ou ajouter un autre paramètre àdoSomething() contrôler l'archivage, brisant ainsi votre code, etc.

Le problème est que le code est trop étroitement lié. Passer un coup pour contrôler le comportement est, à mon avis, le signe d'une mauvaise API. Si vous possédez l'API, changez-la. Deux méthodes, doSomething()et doSomethingWithUndo(), serait mieux. si vous ne le possédez pas, écrivez vous-même deux méthodes d'encapsulation dans le code que vous possédez et faites appel à l'une d'elles doSomething(true)et à l'autre doSomething(false).

Kate Gregory
la source
4

L'appelant n'a pas pour rôle de définir le rôle des arguments. Je vais toujours pour la version en ligne, et vérifie la signature de la fonction appelée si j'ai un doute.

La variable doit être nommée d'après son rôle dans la portée actuelle. Non La portée où ils sont envoyés.

Simon Bergot
la source
1
le rôle de la variable dans la portée actuelle est de dire à quoi elle sert. Je ne vois pas en quoi cela contredit l'utilisation de variables temporaires par OP.
SuperM
4

Ce que je ferais en JavaScript est que la fonction prenne un objet comme seul paramètre, comme ceci:

function doSomething(settings) {
    var preventUndo = settings.hasOwnProperty('preventUndo') ? settings.preventUndo : false;
    // Deal with preventUndo in the normal way.
}

Puis appelez-le avec:

doSomething({preventUndo: true});
ridecar2
la source
HA!!! Nous avons tous deux fait une doSomething(x)méthode! lol ... Je n'ai même pas remarqué votre message jusqu'à présent.
Blesh
Que se passerait-il si je passais une chaîne "noUndo", et comment est-il clair pour la personne qui utilise votre signature quels paramètres sont supposés contenir sans regarder dans l'implémentation?
Nick B.
1
Documentez votre code. C'est ce que tout le monde fait. Cela facilite la lecture de votre code, l'écriture ne doit se faire qu'une seule fois.
ridecar2
1
@ NickB. la convention est puissante. Si la plupart de votre code accepte des objets avec des arguments, cela est clair.
orip
4

J'aime utiliser les fonctionnalités linguistiques pour clarifier moi-même. Par exemple, en C #, vous pouvez spécifier les paramètres par leur nom:

 CallSomething(name: "So and so", age: 12, description: "A random person.");

En JavaScript, je préfère généralement utiliser un objet options comme argument pour cette raison:

function doSomething(args) { /*...*/ }

doSomething({ name: 'So and so', age: 12, description: 'A random person.' });

C'est juste ma préférence cependant. Je suppose que cela dépendra beaucoup de la méthode appelée et de la façon dont l'EDI aide le développeur à comprendre la signature.

blesh
la source
1
C'est "l'inconvénient" des langages typés. Vous ne pouvez pas vraiment appliquer une signature sans une validation. Je ne savais pas que vous pouviez ajouter des noms comme celui-là dans JS.
James P.
1
@ JamesPoulson: Vous saviez probablement que vous pouviez le faire dans JS et vous ne le réalisiez pas. Tout est fini dans JQuery: $.ajax({url:'', data:''})etc. etc.
blesh
Vrai. Cela semble inhabituel à première vue, mais il est similaire aux pseudo-objets en ligne pouvant exister dans certaines langues.
James P.
3

Je trouve que c'est le plus simple et le plus facile à lire:

enum Undo { ALLOW, PREVENT }

doSomething(Undo u) {
    if (Undo.ALLOW == u) {
        // save stuff to undo later.
    }
    // do your thing
}

doSomething(Undo.ALLOW);

MainMa n'aime pas ça. Il se peut que nous devions accepter d'être en désaccord ou utiliser une solution proposée par Joshua Bloch:

enum Undo {
    ALLOW {
        @Override
        public void createUndoBuffer() {
            // put undo code here
        }
    },
    PREVENT {
        @Override
        public void createUndoBuffer() {
            // do nothing
        }
    };

    public abstract void createUndoBuffer();
}

doSomething(Undo u) {
    u.createUndoBuffer();
    // do your thing
}

Maintenant, si vous ajoutez un jour Undo.LIMITED_UNDO, votre code ne sera pas compilé à moins que vous n'implémentiez la méthode createUndoBuffer (). Et dans quelque chose () il n'y a pas de si (Undo.ALLOW == u). Je l'ai fait dans les deux sens et la deuxième méthode est assez lourde et un peu difficile à comprendre lorsque l'énumération d'annulation s'étend aux pages et aux pages de code, mais cela vous fait réfléchir. J'utilise généralement la méthode la plus simple pour remplacer un booléen simple par un enum de 2 valeurs jusqu'à ce que j'ai des raisons de changer. Quand j'ajoute une troisième valeur, j'utilise mon IDE pour "Rechercher-usages" et tout réparer.

GlenPeterson
la source
2

Je pense que votre solution rend votre code un peu plus lisible, mais j’éviterais de définir une variable supplémentaire simplement pour rendre votre appel de fonction plus clair. En outre, si vous souhaitez utiliser une variable supplémentaire, je la marquerai comme constante si votre langage de programmation le prend en charge.

Je peux penser à deux alternatives qui n'impliquent pas de variable supplémentaire:

1. Utilisez un commentaire supplémentaire

doSomething(/* preventUndo */ true);

2. Utilisez un enum à deux valeurs au lieu de boolean

enum Options
{
    PreventUndo,
    ...
}

...

doSomething(PreventUndo);

Vous pouvez utiliser la variante 2 si votre langue prend en charge les énumérations.

MODIFIER

Bien sûr, utiliser des arguments nommés est également une option, si votre langue les prend en charge.

En ce qui concerne le codage supplémentaire requis par les enums, il me semble vraiment négligeable. Au lieu de

if (preventUndo)
{
    ...
}

tu as

if (undoOption == PreventUndo)
{
    ...
}

ou

switch (undoOption)
{
  case PreventUndo:
  ...
}

Et même si la saisie est un peu plus fréquente, rappelez-vous que le code est écrit une fois et lu plusieurs fois. Il est donc intéressant de taper un peu plus maintenant pour trouver un code plus lisible six mois plus tard.

Giorgio
la source
2

Je suis d'accord avec ce que @GlenPeterson a dit:

doSomething(Undo.ALLOW); // call using a self evident enum

mais aussi insteresting serait le suivant puisqu'il me semble qu'il n'y a que deux possibilités (vrai ou faux)

//Two methods    

doSomething()  // this method does something but doesn't prevent undo

doSomethingPreventUndo() // this method does something and prevents undo
Tulains Córdova
la source
2
Belle idée - je n'y avais pas pensé. Lorsque vous devenez plus compliqué, comme doSomething (String, String, String, boolean, boolean, boolean), les constantes nommées sont la seule solution raisonnable. Eh bien, Josh Bloch suggère un objet fabrique et constructeur comme suit: MyThingMaker thingMaker = MyThingMaker.new (); thingMaker.setFirstString ("Hi"); thingMaker.setSecondString ("Là"); etc ... Chose t = choseMaker.makeIt (); t.doQuelque chose (); C'est juste beaucoup plus de travail, alors ça vaut le coup.
GlenPeterson
Un problème avec cette dernière approche est qu’il est difficile d’écrire une méthode qui utilise doSomethingmais permet à l’appelant d’autoriser ou non l’annulation. Un remède peut consister à avoir une surcharge qui utilise l'option booléenne, mais également à avoir des versions wrapper qui appellent l'ancienne version avec le paramètre toujours vrai ou toujours faux.
Supercat
@GlenPeterson Une usine est une possibilité et est possible en Javascript (mentionné par l'OP).
James P.
2

Puisque vous vous interrogez sur le javascript principalement, en utilisant coffeescript, vous pouvez avoir un argument nommé comme syntaxe, super facile:

# declare method, ={} declares a default value to prevent null reference errors
doSomething = ({preventUndo} = {}) ->
  if preventUndo
    undo = no
  else
    undo = yes

#call method
doSomething preventUndo: yes
#or 
doSomething(preventUndo: yes)

compile pour

var doSomething;

doSomething = function(_arg) {
  var preventUndo, undo;
  preventUndo = (_arg != null ? _arg : {}).preventUndo;
  if (preventUndo) {
    return undo = false;
  } else {
    return undo = true;
  }
};

doSomething({
  preventUndo: true
});

doSomething({
  preventUndo: true
});

Amusez-vous à http://js2coffee.org/ pour essayer les possibilités

Guillaume86
la source
J'utilise coffeeScript uniquement lorsque j'ai besoin d'éviter la folie OOP de javascript. C’est une bonne solution que je pourrais utiliser lors du codage seul, il serait difficile de justifier à un collègue que je passe un objet au lieu d’un booléen pour des raisons de lisibilité!
methodofaction
1

Juste pour une solution moins conventionnelle et depuis l’OP mentionnée Javascript, une possibilité est d’utiliser un tableau associatif pour mapper des valeurs. L'avantage par rapport à un booléen unique est que vous pouvez avoir autant d'arguments que vous le souhaitez sans vous soucier de leur séquence.

var doSomethingOptions = new Array();

doSomethingOptions["PREVENTUNDO"] = true;
doSomethingOptions["TESTMODE"] = false;

doSomething( doSomethingOptions );

// ...

function doSomething( doSomethingOptions ){
    // Check for null here
    if( doSomethingOptions["PREVENTUNDO"] ) // ...
}

Je me rends compte que c’est le réflexe de quelqu'un issu d’un arrière-plan très typé et que ce n’est peut-être pas si pratique que cela, mais considérez cela comme une originalité.

Une autre possibilité est d'avoir un objet et est également possible en Javascript. Je ne suis pas sûr à 100% que ce soit parfaitement correct. Regardez des modèles tels que Factory pour plus d'inspiration.

function SomethingDoer( preventUndo ) {
    this.preventUndo = preventUndo;
    this.doSomething = function() {
            if( this.preventUndo ){
                    // ...
            }
    };
}

mySomethingDoer = new SomethingDoer(true).doSomething();
James Poulson
la source
1
Je pense que cela peut être mieux écrit en notation par points ( doSomethingOptions.PREVENTUNDO) au lieu de la notation par accès à un tableau.
Paŭlo Ebermann
1

L'objectif de votre question et des autres réponses est d'améliorer la lisibilité lorsque la fonction est appelée, ce qui est un objectif avec lequel je suis d'accord. Toute directive spécifique, événement "pas d'argument booléen", doit toujours servir de moyen à cette fin et ne pas devenir et se terminer elle-même.

Je pense qu'il est utile de noter que ce problème est généralement évité lorsque le langage de programmation prend en charge les arguments nommés / arguments de mot clé, comme en C # 4 et Python, ou lorsque les arguments de méthode sont entrelacés dans le nom de la méthode, comme dans Smalltalk ou Objective-C.

Exemples:

// C# 4
foo.doSomething(preventUndo: true);
# Python
foo.doSomething(preventUndo=True)
// Objective-C
[foo doSomethingWith:bar shouldPreventUndo:YES];
orip
la source
0

Il faut éviter de passer des variables booléennes comme arguments à des fonctions. Parce que les fonctions doivent faire une chose à la fois. En passant une variable booléenne, la fonction a deux comportements. Cela crée également un problème de lisibilité à un stade ultérieur ou pour les autres programmeurs qui ont tendance à voir votre code. Cela crée également un problème lors du test de la fonction. Dans ce cas, vous devez probablement créer deux cas de test. Imaginons que si une fonction possède une instruction switch et modifie son comportement en fonction du type de commutateur, vous devez définir autant de scénarios de test différents.

Chaque fois que l'on trouve un argument booléen pour la fonction, il doit modifier le code de manière à ne pas écrire d'argument booléen.

Ganji
la source
Donc, vous convertiriez une fonction avec un argument booléen en deux fonctions qui ne diffèrent que par une infime partie de leur code (où l'original aurait un if(param) {...} else {...})?
Paŭlo Ebermann
-1
int preventUndo = true;
doSomething(preventUndo);

Un bon compilateur pour les langages à faible niveau de niveau tels que C ++ optimisera le code machine de la ligne supérieure à 2 comme ci-dessous:

doSomething(true); 

donc, peu importe la performance, cela augmentera la lisibilité.

En outre, il est utile d’utiliser une variable au cas où elle deviendrait variable ultérieurement et accepterait également d’autres valeurs.

Akash Agrawal
la source