J'ai une grande méthode qui fait 3 tâches, chacune d'elles peut être extraite dans une fonction distincte. Si je crée des fonctions supplémentaires pour chacune de ces tâches, cela améliorera-t-il mon code ou non et pourquoi?
Évidemment, cela fera moins de lignes de code dans la fonction principale, mais il y aura des déclarations de fonctions supplémentaires, donc ma classe aura des méthodes supplémentaires, ce qui, je pense, n'est pas bon, car cela rendra la classe plus complexe.
Dois-je faire cela avant d'avoir écrit tout le code ou dois-je le laisser jusqu'à ce que tout soit fait, puis extraire les fonctions?
java
design-patterns
dhblah
la source
la source
Réponses:
Il s'agit d'un livre auquel je renvoie souvent, mais je recommence: le code propre de Robert C. Martin , chapitre 3, "Fonctions".
Préférez-vous lire une fonction avec +150 lignes ou une fonction appelant 3 fonctions +50 lignes? Je pense que je préfère la deuxième option.
Oui , cela rendra votre code meilleur dans le sens où il sera plus "lisible". Faites des fonctions qui effectuent une et une seule chose, elles seront plus faciles à maintenir et à produire un cas de test pour.
Aussi, une chose très importante que j'ai apprise avec le livre susmentionné: choisissez des noms bons et précis pour vos fonctions. Plus la fonction est importante, plus le nom doit être précis. Ne vous inquiétez pas de la longueur du nom, s'il doit être appelé
FunctionThatDoesThisOneParticularThingOnly
, nommez-le ainsi.Avant d'effectuer votre refactorisation, écrivez un ou plusieurs cas de test. Assurez-vous qu'ils fonctionnent. Une fois votre refactoring terminé, vous pourrez lancer ces cas de test pour vous assurer que le nouveau code fonctionne correctement. Vous pouvez écrire des tests "plus petits" supplémentaires pour vous assurer que vos nouvelles fonctions fonctionnent correctement de manière séparée.
Enfin, et ce n'est pas contraire à ce que je viens d'écrire, demandez-vous si vous avez vraiment besoin de faire ce refactoring, consultez les réponses à " Quand refactoriser ?" (aussi, recherchez les questions SO sur "refactoring", il y en a plus et les réponses sont intéressantes à lire)
Si le code est déjà là et fonctionne et que vous manquez de temps pour la prochaine version, ne le touchez pas. Sinon, je pense que l'on devrait faire de petites fonctions autant que possible et en tant que tel, refactoriser chaque fois qu'un certain temps est disponible tout en s'assurant que tout fonctionne comme avant (cas de test).
la source
Oui évidemment. S'il est facile de voir et de séparer les différentes "tâches" d'une seule fonction.
Mais cela pourrait poser des problèmes:
la source
Le problème que vous posez n'est pas un problème de codage, de conventions ou de pratique de codage, mais plutôt un problème de lisibilité et de la façon dont les éditeurs de texte affichent le code que vous écrivez. Ce même problème apparaît également dans l'article:
Est-il correct de diviser les fonctions et méthodes longues en plus petites, même si elles ne seront appelées par rien d'autre?
La division d'une fonction en sous-fonctions est logique lors de la mise en œuvre d'un grand système dans le but d'encapsuler les différentes fonctionnalités qui le composeront. Néanmoins, tôt ou tard, vous vous retrouverez avec un certain nombre de grandes fonctions. Certains d'entre eux sont illisibles et difficiles à entretenir que vous les conserviez en tant que fonctions longues simples ou que vous les divisiez en fonctions plus petites. Cela est particulièrement vrai pour les fonctions où les opérations que vous effectuez ne sont nécessaires à aucun autre endroit de votre système. Permet de ramasser une fonction aussi longue et de la considérer dans une vue plus large.
Pro:
Contra:
Imaginons maintenant de diviser la fonction longue en plusieurs sous-fonctions et de les regarder avec une perspective plus large.
Pro:
Contra:
Les deux solutions ont des avantages et des inconvénients. La meilleure solution serait d'avoir des éditeurs qui permettent d'étendre, en ligne et pour toute la profondeur, chaque fonction appeler dans son contenu. Ce qui ferait de la division des fonctions en sous-fonctions la seule meilleure solution.
la source
Pour moi, il y a quatre raisons d'extraire des blocs de code en fonctions:
Vous le réutilisez : vous venez de copier un bloc de code dans le presse-papiers. Au lieu de simplement le coller, mettez-le dans une fonction et remplacez le bloc par un appel de fonction des deux côtés. Ainsi, chaque fois que vous devez modifier ce bloc de code, vous devez uniquement modifier cette fonction unique au lieu de modifier le code à plusieurs endroits. Ainsi, chaque fois que vous copiez un bloc de code, vous devez créer une fonction.
C'est un rappel : c'est un gestionnaire d'événements ou une sorte de code utilisateur une bibliothèque ou un framework appelle. (Je peux difficilement imaginer cela sans faire de fonctions.)
Vous pensez qu'il sera réutilisé , dans le projet en cours ou peut-être ailleurs: vous venez d'écrire un bloc qui calcule la plus longue sous-séquence commune de deux tableaux. Même si votre programme n'appelle cette fonction qu'une seule fois, je pense que j'aurai éventuellement besoin de cette fonction dans d'autres projets également.
Vous voulez du code auto-documenté : Donc, au lieu d'écrire une ligne de commentaire sur un bloc de code résumant ce qu'il fait, vous extrayez le tout dans une fonction et nommez-le ce que vous écririez dans un commentaire. Bien que je ne sois pas fan de cela, parce que j'aime écrire le nom de l'algorithme utilisé, la raison pour laquelle j'ai choisi cet algorithme, etc. Les noms de fonctions seraient alors trop longs ...
la source
Je suis sûr que vous avez entendu l'avis selon lequel les variables doivent être définies aussi étroitement que possible, et j'espère que vous êtes d'accord avec cela. Eh bien, les fonctions sont des conteneurs de portée, et dans les fonctions plus petites, la portée des variables locales est plus petite. Il est beaucoup plus clair comment et quand ils sont censés être utilisés et il est plus difficile de les utiliser dans le mauvais ordre ou avant leur initialisation.
De plus, les fonctions sont des conteneurs de flux logique. Il n'y a qu'une seule entrée, les sorties sont clairement marquées et si la fonction est suffisamment courte, les flux internes devraient être évidents. Cela a pour effet de réduire la complexité cyclomatique qui est un moyen fiable de réduire le taux de défauts.
la source
En plus: J'ai écrit ceci en réponse à la question de dallin (maintenant fermée) mais je pense toujours que cela pourrait être utile à quelqu'un alors voilà
Je pense que la raison des fonctions d'atomisation est double, et comme @jozefg le mentionne dépend du langage utilisé.
Séparation des préoccupations
La raison principale pour cela est de garder différents morceaux de code séparés, donc tout bloc de code qui ne contribue pas directement au résultat / intention souhaité de la fonction est une préoccupation distincte et pourrait être extrait.
Supposons que vous ayez une tâche d'arrière-plan qui met également à jour une barre de progression, la mise à jour de la barre de progression n'est pas directement liée à la tâche de longue durée et doit donc être extraite, même si c'est le seul morceau de code qui utilise la barre de progression.
Dites en JavaScript que vous avez une fonction getMyData (), qui 1) construit un message de savon à partir de paramètres, 2) initialise une référence de service, 3) appelle le service avec le message de savon, 4) analyse le résultat, 5) renvoie le résultat. Cela semble raisonnable, j'ai écrit cette fonction exacte plusieurs fois - mais cela pourrait vraiment être divisé en 3 fonctions privées, y compris le code pour 3 et 5 (si cela), car aucun des autres codes n'est directement responsable de l'obtention des données du service .
Expérience de débogage améliorée
Si vous avez des fonctions complètement atomiques, votre trace de pile devient une liste de tâches, répertoriant tout le code exécuté avec succès, c'est-à-dire:
serait beaucoup plus intéressant que de découvrir qu'il y avait une erreur lors de l'obtention des données. Mais certains outils sont encore plus utiles pour déboguer des arborescences d'appels détaillées que cela, par exemple, Microsofts Debugger Canvas .
Je comprends également vos préoccupations selon lesquelles il peut être difficile de suivre le code écrit de cette façon, car à la fin de la journée, vous devez choisir un ordre de fonctions dans un seul fichier alors que votre arborescence d'appels serait plus complexe que celle . Mais si les fonctions sont bien nommées (intellisense me permet d'utiliser 3-4 mots de casse camal dans n'importe quelle fonction que je veux sans me ralentir) et structurées avec une interface publique en haut du fichier, votre code se lira comme un pseudo-code qui est de loin le moyen le plus simple d'obtenir une compréhension de haut niveau d'une base de code.
Pour info - c'est une de ces choses "fais ce que je dis pas comme je fais", garder le code atomique est inutile à moins que vous ne soyez impitoyablement cohérent avec lui à mon humble avis, ce que je ne suis pas.
la source