Devrions-nous éliminer les variables locales si nous le pouvons?


95

Par exemple, pour garder un processeur sous Android, je peux utiliser un code comme celui-ci:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

mais je pense que les variables locales powerManageret wakeLockpeuvent être éliminés:

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

Une scène similaire apparaît en mode alerte iOS, par exemple: de

UIAlertView *alert = [[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil];
[alert show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

à:

[[[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil] show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

Est-ce une bonne pratique d'éliminer une variable locale si elle est utilisée une seule fois dans le périmètre?


95
Pas nécessairement. Parfois, il est plus clair pour le code d’utiliser des variables uniques, et dans la plupart des langages de programmation décents, les coûts d’exécution pour ces variables supplémentaires sont minimes (voire nuls).
Robert Harvey

75
Il est également plus difficile de parcourir le code avec un débogueur. Et vous devez également être sûr (en fonction de la langue) que la première expression n'est pas NULL ou une erreur.
GrandmasterB

78
Non ce n'est pas. En fait, il est recommandé d' introduire des variables locales pour rompre les chaînes d'appels de méthodes. Le code machine généré est susceptible d'être identique, et le code source est presque garanti d'être plus lisible, donc meilleur.
Kilian Foth

48
Essayez ça. Maintenant, branchez le débogueur et essayez de voir l'état de PowerManager ou de WakeLock. Réalisez votre erreur. J'avais l'habitude de penser la même chose ("qu'est-ce qui se passe avec tous ces gens du pays?") Jusqu'à ce que je passe le plus clair de mon temps à rechercher du code.
Faerindel

42
Même dans votre exemple, votre version "éliminée" comporte une barre de défilement et ne tient pas entièrement sur mon écran, ce qui la rend beaucoup plus difficile à lire.
Erik

Réponses:


235

Le code est lu beaucoup plus souvent que ce qui est écrit, vous devriez donc avoir pitié de la pauvre âme qui devra le lire dans six mois (ce sera peut-être vous) et rechercher le code le plus clair et le plus facile à comprendre. À mon avis, la première forme, avec des variables locales, est beaucoup plus compréhensible. Je vois trois actions sur trois lignes, plutôt que trois actions sur une ligne.

Et si vous pensez optimiser quelque chose en vous débarrassant de variables locales, vous ne l'êtes pas. Un compilateur moderne mettra powerManagerdans un registre 1 , qu'une variable locale soit utilisée ou non, pour appeler la newWakeLockméthode. La même chose est vraie pour wakeLock. Donc, vous vous retrouvez avec le même code compilé dans les deux cas.

1 Si vous aviez beaucoup de code intermédiaire entre la déclaration et l'utilisation de la variable locale, cela pourrait aller sur la pile, mais c'est un détail mineur.


2
Vous ne pouvez pas savoir s'il y a beaucoup de code, l'optimiseur pourrait intégrer certaines fonctions ... Sinon, d'accord, rechercher la lisibilité est le bon choix.
Matthieu M.

2
Eh bien, je pense le contraire, si je vois des variables locales obtenant des initialisations multiples plusieurs fois sur chaque fonction où elles sont utilisées au lieu d’être des attributs quand c’est possible, je chercherai pourquoi, fondamentalement, je lirai attentivement les lignes et les comparerai assurez-vous qu'ils sont les mêmes. Donc je vais perdre du temps.
Walfrat

15
@Walfrat Des variables locales ont-elles été initialisées plusieurs fois? Aie. Personne n'a suggéré de réutiliser les locaux :)
Luaan

32
Les membres de @Walfrat génèrent des effets secondaires et ne doivent être utilisés que si vous souhaitez spécifiquement conserver l'état entre les appels aux membres publics. Cette question semble traiter de l'utilisation locale pour le stockage temporaire de calculs intermédiaires.
Gusdor

4
Q: Devrions-nous éliminer les variables locales si nous le pouvons? N °
simon

94

Certains commentaires très votants l'ont dit, mais aucune des réponses que j'ai vues ne l'a été. Je vais donc l'ajouter en tant que réponse. Votre facteur principal dans le choix de cette question est:

Débogage

Souvent, les développeurs consacrent beaucoup plus de temps et d’efforts au débogage qu’à l’écriture de code.

Avec les variables locales, vous pouvez:

  • Point d'arrêt sur la ligne où il est affecté (avec toutes les autres décorations de points d'arrêt, telles que les points d'arrêt conditionnels, etc.)

  • Inspecter / regarder / imprimer / changer la valeur de la variable locale

  • Problèmes de capture dus aux lancers.

  • Avoir des traces de pile lisibles (la ligne XYZ a une opération au lieu de 10)

Sans variables locales, toutes les tâches ci-dessus sont beaucoup plus difficiles, extrêmement difficiles ou carrément impossibles, selon votre débogueur.

En tant que tel, suivez la maxime infâme (écrivez le code de la même manière que si le développeur suivant le maintenait après vous-même était un fou fou qui sait où vous habitez) et commettez une erreur de débogage , ce qui signifie utiliser des variables locales. .


20
J'ajouterais que les stacktraces sont beaucoup moins utiles quand il y a beaucoup d'appels sur une seule ligne. Si vous avez une exception de pointeur nul sur la ligne 50 et que 10 appels y sont inscrits, cela ne restreindra pas beaucoup la tâche. Dans une application de production, ce sont souvent l'essentiel des informations que vous obtenez à partir d'un rapport de défaut.
JimmyJames

3
Parcourir le code est également beaucoup plus difficile. S'il existe call () -> call () -> call () -> call (), il est très difficile d'entrer dans le troisième appel de méthode. Beaucoup plus facile s'il y a quatre variables locales
gnasher729

3
@FrankPuffer - nous devons faire face au monde réel. Où les débogueurs ne font pas ce que vous proposez.
DVK

2
@DVK: En fait, il y a plus de débogueurs que je ne le pensais, ce qui vous permet d'inspecter ou de regarder n'importe quelle expression. MS Visual Studio (depuis la version 2013) dispose de cette fonctionnalité pour C ++ et C #. Eclipse en a pour Java. Dans un autre commentaire, Faerindel a mentionné IE11 pour JS.
Frank Puffer

4
@DVK: Oui, beaucoup de débogueurs du monde réel ne font pas ce que je propose. Mais cela n’a rien à voir avec la première partie de mon commentaire. Je trouve juste fou de supposer que la personne qui va maintenir mon code va principalement interagir avec lui au moyen d’un débogueur. Les débogueurs sont utiles à certaines fins, mais s’ils disposent du principal outil d’analyse de code, je dirais que quelque chose ne va pas du tout et que je tenterais de le réparer au lieu de rendre le code plus facile à déboguer.
Frank Puffer

47

Seulement si cela rend le code plus facile à comprendre. Dans vos exemples, je pense que cela rend la lecture plus difficile.

L'élimination des variables dans le code compilé est une opération triviale pour tout compilateur respectable. Vous pouvez inspecter la sortie vous-même pour vérifier.


1
Cela a autant à voir avec le style que l'utilisation de variables. La question a maintenant été modifiée.
Jodrell

29

Votre question "est-ce une bonne pratique d'éliminer une variable locale si elle est utilisée une seule fois dans le périmètre?" teste les mauvais critères. L'utilité d'une variable locale ne dépend pas du nombre d'utilisations mais rend le code plus clair. L'étiquetage de valeurs intermédiaires avec des noms significatifs peut améliorer la clarté dans des situations telles que celle que vous présentez, car il divise le code en fragments plus petits et plus faciles à digérer.

À mon avis, le code non modifié que vous avez présenté est plus clair que votre code modifié et devrait donc rester inchangé. En fait, je considérerais d'extraire une variable locale de votre code modifié afin d'améliorer la clarté.

Je ne m'attendrais pas à un impact sur les performances des variables locales, et même s'il en existe une, elle sera probablement trop petite pour être considérée comme intéressante, à moins que le code ne soit dans une boucle très étroite dans une partie critique du programme.


Je pense que cela a beaucoup à voir avec le style et l'utilisation des espaces blancs. La question a été modifiée.
Jodrell

15

Peut être.

Personnellement, j'hésiterais à éliminer les variables locales si une conversion de type est impliquée. Je trouve votre version compacte moins lisible car le nombre de crochets commence à s’approcher d’une limite mentale que j’ai. Il introduit même un nouvel ensemble de crochets qui n’était pas nécessaire dans la version légèrement plus détaillée qui utilise une variable locale.

La mise en évidence de la syntaxe fournie par l'éditeur de code peut atténuer ces problèmes dans une certaine mesure.

À mes yeux, c'est le meilleur compromis:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc").acquire();

Ainsi, garder une variable locale et abandonner l’autre. En C #, j'utiliserais le mot-clé var sur la première ligne car le transtypage en typographie fournit déjà les informations de type.


13

Bien que j'accepte la validité des réponses en faveur des variables locales, je vais me faire l'avocat du diable et présenter un point de vue opposé.

Personnellement, je suis un peu contre l’utilisation de variables locales à des fins purement documentaires, bien que cette raison indique elle-même que la pratique a de la valeur: les variables locales pseudo-documentent effectivement le code.

Dans votre cas, le problème principal est l’indentation et non l’absence de variables. Vous pouvez (et devriez) formater le code comme suit:

((PowerManager)getSystemService(POWER_SERVICE))
        .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag")
        .acquire();

Comparez cela avec la version avec des variables locales:

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

Avec les variables locales: les noms ajoutent des informations au lecteur et les types sont clairement spécifiés, mais les appels de méthode réels sont moins visibles et (à mon avis), la lecture est moins claire.

Sans utiliser de variables locales: c'est plus concis et les appels de méthode sont plus visibles, mais vous pouvez demander plus d'informations sur ce qui est renvoyé. Certains IDE peuvent toutefois vous le montrer.

Trois autres commentaires:

  1. À mon avis, l'ajout de code qui n'a aucune utilisation fonctionnelle peut parfois être déroutant, car le lecteur doit se rendre compte qu'il n'a en réalité aucune utilisation fonctionnelle et qu'il est simplement là pour la "documentation". Par exemple, si cette méthode comptait 100 lignes, il ne serait pas évident que les variables locales (et donc le résultat de l'appel de la méthode) n'étaient pas nécessaires ultérieurement, à moins que vous ne lisiez la méthode complète.

  2. L'ajout de variables locales signifie que vous devez spécifier leurs types, ce qui introduit dans le code une dépendance qui, autrement, ne serait pas présente. Si le type de retour des méthodes change de manière triviale (par exemple, il a été renommé), vous devrez mettre à jour votre code, alors que sans les variables locales, vous ne le feriez pas.

  3. Le débogage pourrait être plus facile avec des variables locales si votre débogueur n'affiche pas les valeurs renvoyées par les méthodes. Mais la solution à cela est de corriger les défauts des débogueurs, pas de changer le code.


En ce qui concerne vos autres commentaires: 1. Cela ne devrait pas poser de problème si vous suivez la convention voulant que les variables locales soient déclarées aussi proches que possible de leur lieu d'utilisation et que vous gardiez vos méthodes d'une longueur raisonnable. 2. Pas dans un langage de programmation avec inférence de type. 3. Un code bien écrit n'a souvent pas besoin d'un débogueur.
Robert Harvey

2
Votre premier exemple de code ne fonctionne que si vous construisez vos objets à l'aide d'interfaces fluides ou d'un "constructeur", des motifs que je n'utiliserais pas partout dans mon code, mais uniquement de manière sélective.
Robert Harvey

1
Je pense que la question suppose que les variables locales sont fonctionnellement redondantes et que, par conséquent, ce type d'interface est requis pour le cas. Nous pourrions probablement supprimer les locaux s'il en était autrement par diverses contorsions, mais je pensais que nous examinions le cas simple.
rghome

1
Je trouve votre variante encore pire pour la lisibilité. J'ai peut-être été trop exposé à VB.net, mais la première fois que je vois votre échantillon est "Où est passée la déclaration WITH? Est-ce que je l'ai accidentellement supprimée?" J'ai besoin de regarder deux fois ce qui se passe et ce n'est pas bon lorsque je dois revoir le code plusieurs mois plus tard.
Tonny

1
@Tonny pour moi, c'est plus lisible: les locaux n'ajoutent rien qui ne soit pas évident du contexte. J'ai ma tête de Java, donc pas de withdéclaration. Ce serait des conventions de formatage normales en Java.
Rghome

6

Il y a une tension de motivations ici. L'introduction de variables temporaires peut rendre le code plus facile à lire. Mais ils peuvent également empêcher, et rendre plus difficile à voir, d’autres refactorisations possibles, telles que Extract Method et Replace Temp by Query . Le cas échéant, ces derniers types de refactorisation offrent souvent des avantages bien supérieurs à ceux de la variable temp.

Fowler écrit à propos des motivations de ces derniers refactorings :

"Le problème avec les intérimaires est qu’ils sont temporaires et locaux. Parce qu’ils ne peuvent être vus que dans le contexte de la méthode dans laquelle ils sont utilisés, ils ont tendance à encourager les méthodes plus longues, car c’est le seul moyen d’atteindre l’intérimaire. Par En remplaçant le temp par une méthode de requête, toute méthode de la classe peut accéder à l'information. Cela aide beaucoup à créer un code plus propre pour la classe. "

Donc, oui, utilisez les temps temporaires s'ils rendent le code plus lisible, surtout s'il s'agit d'une norme locale pour vous et votre équipe. Mais sachez que cela peut parfois rendre plus difficile la découverte d’améliorations alternatives plus importantes. Si vous pouvez développer votre capacité à sentir quand il est utile de vous passer de tels moments et si vous vous sentez plus à l'aise, alors c'est probablement une bonne chose.

FWIW J'ai personnellement évité de lire le livre de Fowler "Refactoring" pendant dix ans parce que je pensais qu'il n'y avait pas grand-chose à dire sur des sujets aussi simples. J'avais complètement tort. Lorsque j'ai fini par le lire, cela a changé mes pratiques de codage quotidiennes, pour le meilleur.


1
Très bonne réponse. Le meilleur code que j'ai écrit (et que j'ai vu d'autres) contenait souvent beaucoup de petites méthodes (2, 3 lignes) qui pourraient remplacer ces variables temporaires. Cette méthode controversée concerne les méthodes privées qui puent ... Bien pensée et je l’ai souvent trouvée vraie.
Stijn de Witt

Les temps dans cette question se réfèrent déjà à des fonctions, donc cette réponse est sans importance pour la question des OP.
user949300

@ user949300 Je ne pense pas que ce soit hors de propos. Oui, le temps dans l'exemple spécifique de l'OP sont les valeurs de retour de fonctions ou de méthodes. Mais le PO est très clair, ce n’est qu’un exemple de scénario. La question qui se pose est la suivante: "devrions-nous éliminer les variables temporaires quand nous le pouvons?"
Jonathan Hartley

1
OK, je suis vendu, ** a essayé de ** changer mon vote. Mais souvent, lorsque je dépasse le cadre limité de la question du PO, je me sens sonné. Tellement peut être capricieux ... :-). Euh, je ne peux pas changer mon vote avant de l'avoir édité, peu importe ...
user949300

@ user949300 Sainte vache! Une personne qui change d’avis après un examen réfléchi des problèmes! Vous êtes une rareté, monsieur, et je vous retire ma casquette.
Jonathan Hartley

3

Bien, c’est une bonne idée d’éliminer les variables locales si vous pouvez ainsi rendre le code plus lisible. Votre longue ligne n’est pas lisible, mais elle suit un joli style OO. Si vous pouviez le réduire à quelque chose comme

acquireWakeLock(POWER_SERVICE, PARTIAL, "abc");

alors je dirais que ce serait probablement une bonne idée. Peut-être pouvez-vous introduire des méthodes d'assistance pour résumer le problème en quelque chose de similaire; si un code comme celui-ci apparaît plusieurs fois, il pourrait en valoir la peine.


2

Considérons la loi de Demeter ici. L'article de Wikipédia sur LoD indique ce qui suit:

La loi de Demeter pour les fonctions exige qu'une méthode m d'un objet O ne puisse invoquer que les méthodes des types d'objets suivants: [2]

  1. O lui-même
  2. paramètres de m
  3. Tous les objets créés / instanciés dans m
  4. Les objets composants directs de O
  5. Une variable globale, accessible par O, dans le domaine de m

L’application de cette loi a notamment pour conséquence d’éviter d’invoquer des méthodes d’objets renvoyés par d’autres méthodes dans une longue chaîne pointillée, comme le montre le deuxième exemple ci-dessus:

((PowerManager)getSystemService(POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag").acquire();

C'est vraiment difficile de comprendre ce que cela fait. Pour le comprendre, vous devez comprendre ce que fait chaque procédure, puis déchiffrer quelle fonction est appelée sur quel objet. Le premier bloc de code

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock=powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

indique clairement ce que vous essayez de faire: vous obtenez un objet PowerManager, vous obtenez un objet WakeLock à partir de PowerManager, puis vous acquérez le wakeLock. La règle n ° 3 de LoD ci-dessus est la suivante: vous instanciez ces objets dans votre code et vous pouvez donc les utiliser pour obtenir les résultats dont vous avez besoin.

Une autre façon de penser est peut-être de se rappeler que lors de la création d'un logiciel, vous devez écrire par souci de clarté et non par souci de concision. 90% de tout le développement de logiciels est la maintenance. N'écrivez jamais un morceau de code que vous ne voudriez pas maintenir.

Bonne chance.


3
J'aimerais savoir comment la syntaxe fluide s'intègre dans cette réponse. Avec un formatage correct, la syntaxe fluide est hautement lisible.
Gusdor

12
Ce n'est pas ce que "Loi de Demeter" signifie. La loi de Demeter n'est pas un exercice de comptage de points; cela signifie essentiellement "ne parlez qu'à vos amis immédiats".
Robert Harvey

5
Accéder aux mêmes méthodes et aux mêmes membres via une variable intermédiaire reste une violation tout aussi grave de la loi de Demeter. Vous venez de l'obscurcir, pas de le réparer.
Jonathan Hartley

2
En termes de "loi de Demeter", les deux versions sont également "mauvaises".
Hulk

@Gusdor a accepté, cela plus un commentaire sur le style dans l'exemple de question. La question a maintenant été modifiée.
Jodrell

2

Une chose à noter est que le code est lu fréquemment, à différentes "profondeurs". Ce code:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

est facile à "écrémer". C'est 3 déclarations. Nous commençons par un PowerManager. Ensuite, nous arrivons avec un WakeLock. Ensuite nous acquirele wakeLock. Je peux voir cela très facilement simplement en regardant le début de chaque ligne; Les affectations de variables simples sont vraiment faciles à reconnaître partiellement comme "Type varName = ..." et il suffit de parcourir mentalement le "...". De même, la dernière affirmation n’est évidemment pas la forme de la cession, mais elle n’implique que deux noms, l’essentiel est donc immédiatement apparent. Souvent, c'est tout ce dont j'avais besoin de savoir si j'essayais simplement de répondre "à quoi sert ce code?" à un niveau élevé.

Si je suis à la recherche d'un bogue subtil dont je pense qu'il est ici, il est évident que je devrai l'examiner plus en détail et que je me souviendrai en réalité des "..." Mais la structure de déclaration séparée m'aide toujours à faire cette déclaration à la fois (particulièrement utile si je dois approfondir la mise en œuvre des éléments appelés dans chaque déclaration; lorsque je reviens, j'ai bien compris «une unité» et peut ensuite passer à la déclaration suivante).

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

Maintenant, tout est une déclaration. La structure de niveau supérieur n'est pas si facile à lire en écrivant; dans la version originale du PO, sans sauts de ligne ni indentation pour communiquer visuellement une structure, il aurait fallu compter les parenthèses pour le décoder en une séquence de 3 étapes. Si certaines expressions à plusieurs parties sont imbriquées les unes dans les autres plutôt que disposées en chaîne d'appels de méthode, elles risquent de ressembler à cela. Je dois donc faire attention à ne pas faire confiance à cela sans compter les parenthèses. Et si je me fie à l’indentation et que je passe à la dernière chose présumée de tout cela, que .acquire()me dit-il en soi?

Parfois cependant, cela peut être ce que vous voulez. Si j'applique votre transformation à mi-chemin et écrit:

WakeLock wakeLock =
     ((PowerManeger)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLockTage");
wakeLock.acquire();

Maintenant, cela communique en un rapide "obtenir un WakeLock, puis acquireil". Encore plus simple que la première version. Il est immédiatement évident que la chose acquise est un WakeLock. Si obtenir un PowerManagerest juste un détail assez insignifiant par rapport à l’objet de ce code, mais wakeLockc’est important, alors il pourrait être utile d’enterrer le PowerManagercontenu, il est donc naturel de le survoler si vous essayez juste d’obtenir rapidement idée de ce que ce code fait. Et ne pas le nommer communique qu'il n'est utilisé qu'une seule fois, et parfois c'est ce qui est important (si le reste de la portée est très longue, je devrais lire tout cela pour savoir si elle est utilisée à nouveau; bien que l'utilisation de sous-portées explicites puisse être un autre moyen de résoudre ce problème, si votre langage le permet

Ce qui montre que tout dépend du contexte et de ce que vous voulez communiquer. Comme pour l'écriture de prose en langage naturel, il existe toujours de nombreuses façons d'écrire un code donné qui sont fondamentalement équivalentes en termes de contenu d'information. Comme pour l'écriture de prose en langage naturel, vous ne devez généralement pas appliquer de règles mécanistes telles que "éliminer toute variable locale ne se produisant qu'une seule fois". Plutôt commentvous choisissez d'écrire votre code mettra l'accent sur certaines choses et de moins souligner d'autres. Vous devez vous efforcer de faire ces choix consciemment (y compris le choix d'écrire du code moins lisible pour des raisons techniques parfois), en fonction de ce que vous voulez réellement souligner. Pensez en particulier à ce qui sera utile aux lecteurs qui doivent juste "comprendre l'essentiel" de votre code (à différents niveaux), car cela se produira beaucoup plus souvent qu'une lecture très proche expression par expression.


Pour moi, même si je n'ai aucune connaissance de l'API, il est très évident que le code fonctionne, même sans les variables. getSystemService(POWER_SERVICE)obtient le gestionnaire de puissance. .newWakeLockobtient un verrou de réveil. .acquirel'acquiert. OK, je ne connais pas tous les types, mais je peux le savoir dans l'IDE si j'en ai besoin.
rghome

Ce que le code est censé faire peut être évident pour vous , mais vous ne savez pas vraiment ce qu'il fait. Si je suis à la recherche d'un bug, tout ce que je sais est que certains de code quelque part ne fait pas ce qu'il est censé faire, et je dois trouver quel code.
gnasher729

@ gnasher729 Oui. La chasse aux bogues est un exemple que j'ai donné lorsque vous devez lire attentivement tous les détails. Et l'une des choses qui aident vraiment à trouver le code qui ne fait pas ce qu'il est censé faire est de pouvoir voir facilement quelle était l'intention de l'auteur initial.
Ben

quand je reviens, j'ai parfaitement compris «une unité» et je peux ensuite passer à la déclaration suivante. En fait non. En fait, les variables locales empêchent cette compréhension totale. Parce qu'ils persistent encore ... occupant un espace mental ... que va-t-il leur arriver dans les 20 prochaines déclarations ... roulement de tambour ... Sans les habitants, vous seriez sûr que les objets ont disparu et qu'il est impossible de faire quoi que ce soit avec eux dans les lignes suivantes. Pas besoin de se souvenir d'eux. J'aime returnaussi rapidement que possible les méthodes, pour la même raison.
Stijn de Witt

2

C'est même un antipattern avec son propre nom: Train Wreck . Plusieurs raisons d'éviter le remplacement ont déjà été indiquées:

  • Plus difficile à lire
  • Plus difficile à déboguer (pour surveiller les variables et détecter les emplacements des exceptions)
  • Violation de la loi de Demeter (LoD)

Considérez si cette méthode en sait trop sur les autres objets. Le chaînage de méthodes est une alternative qui pourrait vous aider à réduire le couplage.

Rappelez-vous également que les références aux objets sont vraiment bon marché.


Ehm, le code révisé ne fait-il pas que la méthode soit chaînée? EDIT lire l'article lié afin que je vois la différence maintenant. Très bon article merci!
Stijn de Witt

Merci de l'avoir examiné. Quoi qu'il en soit, l'enchaînement de méthodes peut fonctionner dans certains cas, tandis que le fait de laisser la variable peut être la décision appropriée. Il est toujours bon de savoir pourquoi les gens ne sont pas d’accord avec votre réponse.
Borjab

Je suppose que le contraire de "Train Wreck" est "Local Line". C'est ce train qui relie deux grandes villes et qui s'arrête dans chaque village à la campagne, au cas où quelqu'un voudrait monter ou descendre, même si la plupart du temps, personne ne le fait.
rghome

1

La question posée est "Devrions-nous éliminer les variables locales si nous le pouvons"?

Non, vous ne devriez pas éliminer simplement parce que vous le pouvez.

Vous devez suivre les directives de codage dans votre boutique.

La plupart du temps, les variables locales facilitent la lecture et le débogage du code.

J'aime ce que tu as fait avec PowerManager powerManager. Pour moi, un minuscule de la classe signifie qu'il ne s'agit que d'un usage unique.

Lorsque vous ne devriez pas le faire, la variable doit conserver des ressources coûteuses. De nombreuses langues ont une syntaxe pour une variable locale qui doit être nettoyée / publiée. En C #, il utilise.

using(SQLconnection conn = new SQLconnnection())
{
    using(SQLcommand cmd = SQLconnnection.CreateCommand())
    {
    }
}

Ce n'est pas vraiment lié aux variables locales mais au nettoyage des ressources ... Je ne suis pas très versé en C # mais je serais surpris de using(new SQLConnection()) { /* ... */ }ne pas être légal aussi (le fait que ce soit utile ou non est différent :).
Stijn de Witt

1

Un aspect important n'a pas été mentionné dans les autres réponses: chaque fois que vous ajoutez une variable, vous introduisez un état mutable. C'est généralement une mauvaise chose car cela rend votre code plus complexe et donc plus difficile à comprendre et à tester. Bien entendu, plus la portée de la variable est petite, plus le problème est petit.

Ce que vous voulez n’est en réalité pas une variable dont la valeur peut être modifiée, mais une constante temporaire. Par conséquent, songez à exprimer cela dans votre code si votre langue le permet. En Java, vous pouvez utiliser finalet en C ++, vous pouvez utiliser const. Dans la plupart des langages fonctionnels, il s'agit du comportement par défaut.

Il est vrai que les variables locales sont le type d’état modifiable le moins préjudiciable. Les variables membres peuvent causer beaucoup plus de problèmes et les variables statiques sont encore pires. Néanmoins, je trouve important d'exprimer aussi précisément que possible dans votre code ce qu'il est censé faire. Et il existe une énorme différence entre une variable qui pourrait être modifiée ultérieurement et un simple nom pour un résultat intermédiaire. Donc, si votre langage vous permet d'exprimer cette différence, faites-le.


2
Je n’ai pas baissé la voix de votre réponse, mais j’imagine que cela tient au fait que les variables locales ne sont généralement pas considérées comme des «états» dans les langages impératifs, à moins que vous ne parliez d’une méthode longue. Je suis d'accord sur l'utilisation de final / const en tant que meilleure pratique, mais il est plutôt improbable que l'introduction d'une variable qui divise simplement un appel chaîné entraîne des problèmes liés à l'état mutable. En fait, l'utilisation de variables locales permet de gérer les états mutables. Si une méthode peut renvoyer différentes valeurs à différents moments, vous pouvez sinon vous retrouver avec des bugs vraiment intéressants.
JimmyJames

@ JimmyJames: ont ajouté quelques explications à ma réponse. Mais il y a une partie de votre commentaire que je n'ai pas comprise: "l'utilisation de variables locales aide à gérer l'état mutable". Pourriez-vous expliquer cela?
Frank Puffer

1
Par exemple, supposons que je doive vérifier une valeur et, si elle dépasse 10, déclencher une alerte. Supposons que cette valeur vienne de method getFoo(). Si j'évite une déclaration locale, je vais me retrouver avec if(getFoo() > 10) alert(getFoo());Mais getFoo () peut renvoyer des valeurs différentes pour les deux appels différents. Je pourrais envoyer une alerte avec une valeur inférieure ou égale à 10, ce qui est déroutant au mieux et qui me reviendra comme un défaut. Ce genre de chose devient plus important avec la concurrence. L'affectation locale est atomique.
JimmyJames

Très bon point. Peut - être la raison pour laquelle je n'aime pas ces gens du pays ... Allez-vous faire quelque chose avec cette PowerManagerinstance après avoir appelé cette méthode? Laissez-moi vérifier le reste de la méthode… mmm ok non. Et cette WakeLockchose que tu as… que fais-tu avec ça (encore une fois, balaie le reste de la méthode)… mmm encore rien… ok alors pourquoi sont-ils là? Oh oui, censé être plus lisible ... mais sans eux, je suis sûr que vous ne les utilisez plus, je n'ai donc pas à lire le reste de la méthode.
Stijn de Witt

Lors d'une conversation récente, le conférencier a fait valoir que le meilleur moyen de démontrer qu'une variable est définitive est d'écrire des méthodes courtes dans lesquelles il est évident que la variable ne change pas . Si finalnécessaire sur une variable locale dans une méthode, votre méthode est trop longue.
user949300
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.