J'entends beaucoup parler de garder les méthodes courtes et j'ai entendu beaucoup de programmeurs dire que l'utilisation de balises #region dans une méthode est un signe certain qu'elle est trop longue et devrait être refactorisée en plusieurs méthodes. Cependant, il me semble qu'il existe de nombreux cas où la séparation du code avec des balises #region au sein d'une méthode est la meilleure solution pour la refactorisation en plusieurs méthodes.
Supposons que nous ayons une méthode dont le calcul peut être séparé en trois phases plutôt distinctes. De plus, chacune de ces étapes n'est pertinente que pour le calcul de cette méthode, et donc les extraire dans de nouvelles méthodes ne nous permet pas de réutiliser le code. Quels sont donc les avantages d'extraire chaque phase dans sa propre méthode? Pour autant que je sache, tout ce que nous gagnons est une certaine lisibilité et une portée variable distincte pour chaque phase (ce qui aidera à empêcher les modifications d'une phase particulière de casser accidentellement une autre phase).
Cependant, les deux peuvent être atteints sans extraire chaque phase dans sa propre méthode. Les balises de région nous permettent de réduire le code dans un formulaire tout aussi lisible (avec l'avantage supplémentaire que nous n'avons plus à laisser notre place dans ce fichier si nous décidons de développer et d'examiner le code), et d'envelopper simplement chaque phase dans {}
crée sa propre portée pour travailler avec.
L'avantage de le faire de cette façon est que nous ne polluons pas la portée au niveau de la classe avec trois méthodes qui ne sont en fait pertinentes que pour le fonctionnement interne d'une quatrième méthode. Refactoriser immédiatement une méthode longue en une série de méthodes courtes me semble être la réutilisation de code équivalente à une optimisation prématurée; vous introduisez une complexité supplémentaire afin de résoudre un problème qui, dans de nombreux cas, ne se pose jamais. Vous pouvez toujours extraire l'une des phases dans sa propre méthode ultérieurement si l'occasion de réutilisation du code se présente.
Pensées?
la source
#region
balises, mais je désactive complètement le pliage de code dans Visual Studio. Je n'aime pas le code qui essaie de me cacher.Réponses:
Tout ce dont vous devriez vous soucier, c'est que votre code soit utilisable et non réutilisable. Un singe peut transformer du code utilisable en code réutilisable, s'il y a des transformations à faire.
L'argument "j'ai besoin de cela seulement ici" est pauvre, pour le dire poliment. La technique que vous décrivez est souvent appelée la technique des gros titres et est généralement mal vue.
Également pertinent: les réflexions de Jeff Atwoods sur le pliage de code
la source
Ce ne sont pas les régions dans les méthodes qui posent problème, mais le cas quand il y a un besoin (ou même une utilisation) de mettre des régions dans les méthodes.
Ayant dû déboguer de nombreuses méthodes de ligne 200-300, je peux vous dire que je ne le souhaiterais à personne. Si vous écrivez et prenez soin de votre propre code, c'est bien - faites ce que vous voulez. Mais une fois que quelqu'un d'autre l'examine, il doit être immédiatement clair ce que fait une méthode.
Non, ce n'est pas suffisant. Décomposez-le en régions autant que vous voulez, mais je secoue toujours la tête en y pensant.
Si vous entrez dans les méthodes, vous obtenez de nombreux avantages:
///
) et entrez la description, les paramètres, le type de retour, et aussiexception
,example
,remarks
nœuds détail ces scénarios. C'est là que ça va, c'est pour ça!{}
, mais dois-je vérifier chaque méthode pour vous assurer que vous n'avez pas «triché»? Est-ce quedodgyObject
j'utilise ici uniquement parce qu'il est utilisé dans cette région, ou est-ce que cela vient d'ailleurs? Si j'étais dans ma propre méthode, je serais facilement en mesure de voir si elle m'a été transmise ou si je l'ai créée moi-même (ou plutôt, si vous l'avez créée).TurnThingsUpsideDown
méthode a échoué - elle échoue probablement en tournant une mauvaise chose" est beaucoup mieux que "Oh, laDoEverything
méthode a échoué - cela aurait pu être 50 choses différentes et je vais devoir fouiller pendant une heure pour la trouver" .Tout cela avant tout problème de réutilisabilité ou d'improvisibilité. Des méthodes correctement séparées facilitent évidemment la réutilisation, et vous permettent également de remplacer facilement une méthode qui ne fonctionne pas (trop lente, trop boguée, dépendance changée, etc.). Avez-vous déjà essayé de refactoriser l'une de ces énormes méthodes? Il n'y a aucun moyen de savoir que ce que vous changez n'affectera rien d'autre.
Veuillez encapsuler correctement votre logique dans des méthodes de taille raisonnable. Je sais qu'il est facile pour eux de devenir incontrôlable, et affirmer que cela ne vaut pas la peine d'être repensé une fois à ce stade est un autre problème. Mais vous devez accepter le fait qu'il n'y a aucun mal et au moins un avantage potentiel à avoir des méthodes correctement encapsulées, rédigées de façon claire et simplement conçues.
la source
Si votre méthode est suffisamment complexe pour pouvoir être séparée en trois phases distinctes, non seulement ces méthodes devraient-elles être trois méthodes distinctes ... mais votre méthode devrait en fait être une classe distincte, dédiée à ce calcul.
"Mais alors ma classe n'aura qu'une seule méthode publique!"
Vous avez raison, et il n'y a rien de mal à cela. L'idée du principe de responsabilité unique est que votre classe fait une chose .
Votre classe peut appeler chacune des trois méthodes à tour de rôle et renvoyer le résultat. Une chose.
En bonus, votre méthode est désormais testable car ce n'est plus une méthode privée.
Mais peu importe une classe; en réalité, vous devriez en avoir quatre : un pour chacun des éléments de votre méthode, plus un qui prend chacun des trois comme dépendance et les appelle dans le bon ordre, renvoyant le résultat.
Cela rend vos classes encore plus testables. Cela vous permet également de changer facilement l'une de ces trois pièces. Écrivez simplement une nouvelle classe héritant de l'ancienne et remplacez le comportement modifié. Ou créez une interface pour le comportement de chacune de vos trois pièces; puis pour en remplacer un, écrivez simplement une nouvelle classe implémentant cette interface et remplacez-la par l'ancienne dans votre configuration de conteneur d'injection de dépendances.
Quelle? Vous n'utilisez pas l'injection de dépendance? Eh bien, vous n'en avez probablement pas encore vu le besoin, car vous ne suivez pas le principe de la responsabilité unique. Une fois que vous aurez commencé, vous constaterez que la façon la plus simple de donner à chaque classe ses dépendances est d'utiliser un conteneur DI.
MODIFIER :
OK, mettons de côté la question du refactoring. Le but de
#region
est de masquer le code , ce qui signifie principalement du code qui n'a pas besoin d'être modifié dans des circonstances normales. Le code généré est le meilleur exemple. Il doit être masqué dans les régions, car vous n'avez généralement pas besoin de le modifier. Si vous en avez besoin, le fait d'agrandir une région vous donne un petit avertissement de style "Here be dragons" pour vous assurer que vous savez ce que vous faites.Par conséquent, je dirais
#region
qu'il ne faut pas l'utiliser au milieu d'une méthode. Si j'ouvre une méthode, je veux voir le code. L'utilisation de #region me donne un autre niveau de masquage dont je n'ai pas besoin, et cela devient une gêne: je déplie la méthode ... et je ne vois toujours pas de code.Si vous craignez que les gens voient la structure de la méthode (et que vous refusez de la refactoriser), ajoutez des commentaires de bannière comme celui-ci:
Ceux-ci aideront une personne parcourant le code à voir les différentes parties de votre méthode sans avoir à gérer les
#region
balises d' extension et de réduction .la source
ctrl+n
Je pense que l'exemple que vous donnez dans votre argument est moins sur YAGNI (vous n'en aurez pas besoin) et plus sur l'écriture d'un code de qualité approprié qui est facilement maintenable.
Dans les premiers cours de programmation, nous apprenons que si une méthode est longue de 700 LOC, elle est probablement trop grande et serait certainement une énorme douleur à essayer et à déboguer.
Deuxièmement, si j'écris les méthodes A, B et C qui ne sont utilisées que dans la méthode D, je peux plus facilement tester unitairement AB et C indépendamment de D, ce qui permet des tests unitaires plus robustes dans les 4 méthodes.
la source
Ce sont deux avantages. Mais l'important, pour moi en tout cas, c'est le débogage . Si vous devez suivre ce code, il est beaucoup plus simple de pouvoir passer au-dessus de deux des trois sections et de ne suivre que celle qui vous intéresse. La refactorisation en méthodes plus petites le permet; les balises de région ne le font pas.
la source
ctrl-f10
- courir vers le curseurMa règle personnelle est que si une méthode est plus longue qu'un écran, disons 40 lignes, elle est trop longue. Si une classe dépasse 500 lignes, elle est trop grande. Je brise parfois ces règles, parfois même par un grand multiple, mais je le regrette généralement dans l'année.
Même une méthode de 20 à 30 lignes devient un peu longue dans la plupart des cas. Je dirais donc que l'utilisation de régions dans une méthode est généralement une mauvaise idée, tout simplement parce qu'il ne serait presque jamais logique de réduire une région de 20 à 30 lignes en plusieurs sous-régions.
La décomposition de fonctions longues selon cette définition présente au moins deux avantages:
Vous pouvez mettre un nom sur ce que font ces sous-fonctions, ce qui clarifie votre pensée actuelle et simplifie la tâche des responsables ultérieurs.
La décomposition en fonctions plus petites vous oblige à ne transmettre que les paramètres dont ces fonctions plus petites ont besoin, de sorte que l'étendue des données qu'elles nécessitent et modifient est très claire. Cela suppose que vous n'accédez pas et ne modifiez pas l'état d'objet dans une centaine de fonctions feuilles différentes, ce que je découragerais également.
D'après mon expérience, ces règles produisent du code qui est plus facile à écrire sans commettre d'erreurs courantes et peut être compris et modifié plus tard sans casser les comportements subtils. Peu importe si la personne qui doit comprendre / modifier le code est quelqu'un d'autre, ou moi-même après avoir dormi et tout oublié.
Je considérerais donc les régions dans les méthodes comme une "odeur de code" indiquant que des pratiques non durables sont appliquées. Comme toujours, il pourrait y avoir des exceptions, mais elles se produisent si rarement qu'elles ne valent presque pas la peine d'être discutées.
la source
C'est reparti ... Il y a plein d'autres sujets sur les programmeurs qui ont abouti à la même discussion.
Vous signalez des arguments très intéressants concernant les fonctions courtes. Ce n'est pas une déclaration populaire, mais je suis avec vous à ce sujet . Après en avoir souvent discuté auparavant, j'ai l'impression que la discussion se résume généralement à savoir si vous vous concentrez principalement sur une encapsulation appropriée ou si vous prenez le développement piloté par les tests au maximum. Ce sont les deux principaux prétendants à ce qui semble se révéler une autre guerre sainte, et je crains que nous ne soyons sous-alimentés.
Toutefois ...
À mon avis, la solution n'est certainement pas d'envelopper les «phases» dans les régions. Le pliage de code est utilisé pour balayer le code sous le tapis. Laissez le code tel quel, cela pourrait vous rappeler que vous voudrez peut-être refactoriser cela plus tard! Pour le relier à un argument dans votre question: comment pourrez-vous voir une opportunité de réutilisation de code si vous ne voyez aucun code? Les longs segments de code devraient être exactement cela, une épine dans l'œil qui donne envie de le refactoriser.
Comme remplacement approprié pour les régions, je suggère d'utiliser des paragraphes de code car Alex Papadimoulis les nomme dans un commentaire . Vous utilisez peut-être déjà une approche similaire. Ce sont simplement des blocs de code avec un en-tête de commentaire, expliquant ce que fait le bloc entier. Vous avez la lisibilité des fonctions, mais vous pouvez utiliser des phrases anglaises espacées normalement, et vous ne perdez aucune encapsulation.
la source
Bien que je convienne qu'il est généralement préférable de refactoriser le code que d'utiliser
#region
, ce n'est pas toujours possible.Pour appuyer cette affirmation, permettez-moi de donner un exemple.
Il est facile de réajuster les régions pour modifier l'ordre ou de les étendre lorsque des champs supplémentaires sont ajoutés à la classe. De toute façon, des commentaires sont nécessaires pour voir rapidement comment la commande est censée fonctionner. Et surtout: je ne vois pas comment le refactoring aidera la lisibilité dans ce cas.
Cependant, ces exceptions sont rares. Habituellement, il est possible de refactoriser, comme le disent de nombreuses autres réponses.
la source
Je ne pense pas qu'il y ait une seule réponse pour expliquer pourquoi certaines personnes ou équipes décident de ne pas utiliser,
#region
mais si je devais en énumérer quelques-unes, ce seraient les principales raisons que j'ai vues.la source