L'utilisation de blocs de portée internes dans un style de fonction est-elle mauvaise?


10

Dans certains cas (assez rares), il existe un risque de:

  • réutiliser une variable qui n'est pas destinée à être réutilisée (voir exemple 1),

  • ou en utilisant une variable au lieu d'une autre, proche sémantiquement (voir l'exemple 2).

Exemple 1:

var data = this.InitializeData();
if (this.IsConsistent(data, this.state))
{
    this.ETL.Process(data); // Alters original data in a way it couldn't be used any longer.
}

// ...

foreach (var flow in data.Flows)
{
    // This shouldn't happen: given that ETL possibly altered the contents of `data`, it is
    // not longer reliable to use `data.Flows`.
}

Exemple 2:

var userSettingsFile = SettingsFiles.LoadForUser();
var appSettingsFile = SettingsFiles.LoadForApp();

if (someCondition)
{
    userSettingsFile.Destroy();
}

userSettingsFile.ParseAndApply(); // There is a mistake here: `userSettingsFile` was maybe
                                  // destroyed. It's `appSettingsFile` which should have
                                  // been used instead.

Ce risque peut être atténué en introduisant un périmètre:

Exemple 1:

// There is no `foreach`, `if` or anything like this before `{`.

{
    var data = this.InitializeData();
    if (this.IsConsistent(data, this.state))
    {
        this.ETL.Process(data);
    }
}

// ...

// A few lines later, we can't use `data.Flows`, because it doesn't exist in this scope.

Exemple 2:

{
    var userSettingsFile = SettingsFiles.LoadForUser();

    if (someCondition)
    {
        userSettingsFile.Destroy();
    }
}

{
    var appSettingsFile = SettingsFiles.LoadForApp();

    // `userSettingsFile` is out of scope. There is no risk to use it instead of
    // `appSettingsFile`.
}

Cela vous semble-t-il faux? Souhaitez-vous éviter une telle syntaxe? Est-ce difficile à comprendre par les débutants?


7
Pourriez-vous dire que chaque "bloc de portée" indépendant devrait plutôt être sa propre méthode ou fonction?
Dan Pichelman

Je dirais «souvent» pas aussi bien implémenté qu'il pourrait l'être (mais probablement pas un problème de «conception»). Parfois, par conception, il est inévitable (par exemple, la portée des exceptions / ressources).
JustinC

Réponses:


35

Si votre fonction est si longue que vous ne pouvez plus reconnaître d'effets secondaires indésirables ou de réutilisation illégale de variables, il est temps de la diviser en fonctions plus petites - ce qui rend inutile une portée interne.

Pour confirmer cela par une expérience personnelle: il y a quelques années, j'ai hérité d'un projet hérité C ++ avec environ 150 000 lignes de code, et il contenait quelques méthodes utilisant exactement cette technique. Et devinez quoi - toutes ces méthodes étaient trop longues. Comme nous avons refactorisé la majeure partie de ce code, les méthodes sont devenues de plus en plus petites, et je suis sûr qu'il ne reste plus de méthodes de "portée interne"; ils ne sont tout simplement pas nécessaires.


4
Mais pourquoi est-ce mauvais? Avoir de nombreuses fonctions nommées est également déroutant.
Kris Van Bael

1
+1 Exactement, si vous avez besoin d'une étendue, vous effectuez probablement une action particulière qui peut être extraite d'une fonction. Kris, il ne s'agit pas de créer beaucoup, beaucoup de fonctions, c'est que lorsque ces cas se produisent (où la portée d'un bloc peut entrer en conflit avec les autres), généralement une fonction aurait dû être utilisée. Je vois cela dans d'autres langues (à savoir JavaScript, avec les anciennes fonctions de l'IFEF au lieu de simples) tout le temps.
Benjamin Gruenbaum

10
@KrisVanBael: "Avoir de nombreuses fonctions nommées est également déroutant" - si vous utilisez des noms descriptifs, c'est tout le contraire qui est vrai. La création de fonctions trop grandes est la principale raison pour laquelle le code devient difficile à maintenir, et encore plus difficile à étendre.
Doc Brown

2
S'il y a tellement de fonctions que les nommer devient un problème, certaines d'entre elles peuvent éventuellement être regroupées dans un nouveau type car elles peuvent avoir une indépendance significative par rapport à leur corps de fonction d'origine. Certains pourraient être si étroitement liés qu'ils pourraient être éliminés. Du coup, il n'y a plus autant de fonctions.
JustinC

3
Toujours pas convaincu. Souvent, les fonctions longues sont mauvaises. Mais dire que chaque besoin de portée nécessite une fonction distincte est trop noir et blanc pour moi.
Kris Van Bael

3

Il est beaucoup plus facile pour un débutant (et tout programmeur) de penser à:

  • ne pas utiliser une variable non pertinente

que de penser à:

  • l'ajout de structures de code visant à éviter de ne pas utiliser une variable non pertinente.

De plus, du point de vue du lecteur, de tels blocs n'ont aucun rôle apparent: leur suppression n'a aucun impact sur l'exécution. Le lecteur peut se gratter la tête en essayant de deviner ce que le codeur voulait réaliser.


2

L'utilisation d'un oscilloscope est techniquement correcte. Mais si vous voulez rendre votre code plus lisible, vous devez extraire cette partie dans une autre méthode et lui donner un nom complet. De cette façon, vous pouvez donner une portée de vos variables et également donner un nom à votre tâche.


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.