Existe-t-il un trop grand nombre de fonctions / méthodes privées?

63

Je comprends l'importance d'un code bien documenté. Mais je comprends aussi l’importance du code auto-documenté . Plus il est facile de lire visuellement une fonction particulière, plus nous pouvons avancer rapidement pendant la maintenance du logiciel.

Cela dit, j'aime séparer les grandes fonctions en d'autres plus petites. Mais je le fais à un point où une classe peut en avoir plus de cinq simplement pour servir une méthode publique. Maintenant, multipliez cinq méthodes privées par cinq publiques et vous obtiendrez environ 25 méthodes cachées qui ne seront probablement appelées qu'une seule fois par ces méthodes publiques.

Bien sûr, il est maintenant plus facile de lire ces méthodes publiques, mais je ne peux pas m'empêcher de penser qu'avoir trop de fonctions est une mauvaise pratique.

[Modifier]

Les gens me demandent pourquoi, à mon avis, avoir trop de fonctions est une mauvaise pratique.

La réponse simple: c'est un instinct.

Ma conviction n’est pas, pour une part, étayée par des heures d’expérience en génie logiciel. C'est juste une incertitude qui m'a donné un "blocage de l'écrivain", mais pour un programmeur.

Dans le passé, je ne programmais que des projets personnels. Ce n'est que récemment que je suis passé aux projets en équipe. Maintenant, je veux m'assurer que les autres peuvent lire et comprendre mon code.

Je n'étais pas sûr de ce qui améliorera la lisibilité. D'un côté, je pensais séparer une grande fonction en une autre plus petite avec des noms intelligibles. Mais il y avait un autre côté de moi qui disait que c'était simplement redondant.

Donc, je demande à ceci de m'éclairer afin de choisir le bon chemin.

[Modifier]

Ci-dessous, j'ai inclus deux versions permettant de résoudre mon problème. Le premier résout le problème en ne séparant pas de gros morceaux de code. Le second fait des choses séparées.

Première version:

public static int Main()
{
    // Displays the menu.
    Console.WriteLine("Pick your option");
    Console.Writeline("[1] Input and display a polynomial");
    Console.WriteLine("[2] Add two polynomials");
    Console.WriteLine("[3] Subtract two polynomials");
    Console.WriteLine("[4] Differentiate two polynomials");
    Console.WriteLine("[0] Quit");
}

Deuxième version:

public static int Main()
{
    DisplayMenu();
}

private static void DisplayMenu()
{
    Console.WriteLine("Pick your option");
    Console.Writeline("[1] Input and display a polynomial");
    Console.WriteLine("[2] Add two polynomials");
    Console.WriteLine("[3] Subtract two polynomials");
    Console.WriteLine("[4] Differentiate two polynomials");
    Console.WriteLine("[0] Quit");
}

Dans les exemples ci-dessus, ce dernier appelle une fonction qui ne sera utilisée qu'une seule fois pendant toute la durée d'exécution du programme.

Remarque: le code ci-dessus est généralisé, mais il est de même nature que mon problème.

Maintenant, voici ma question: laquelle? Est-ce que je choisis le premier ou le second?

Sal
la source
1
"avoir trop de fonctions est une mauvaise pratique."? Pourquoi? Veuillez mettre à jour votre question pour expliquer pourquoi cela vous semble mauvais.
S.Lott
Je recommanderais de lire la notion de «cohésion de classe» - Bob Martin en parle dans «Code propre».
JᴀʏMᴇᴇ
Si vous ajoutez d’autres réponses, divisez et nommez, mais gardons tous à l’esprit que, à mesure que la classe grandit, ses variables membres deviennent de plus en plus similaires à des variables globales. Une façon d'améliorer le problème (en dehors de solutions plus performantes telles que la refactorisation ;-)) est de séparer ces petites méthodes privées en fonctions autonomes si cela est possible (en ce sens qu'elles n'ont pas besoin d'accéder à beaucoup d'état). Certaines langues permettent des fonctions hors classe, d'autres ont des classes statiques. De cette façon, vous pouvez comprendre cette fonction de manière isolée.
Pablo H
Si quelqu'un peut me dire pourquoi "cette réponse n'est pas utile", je vous en serais reconnaissant. On peut toujours apprendre. :-)
Pablo H
@PabloH La réponse que vous avez fournie donne une bonne idée du PO, mais elle ne répond pas à la question qui vous a été posée. Je l'ai déplacé dans le champ des commentaires.
maple_shaft

Réponses:

36

Maintenant, multipliez cinq méthodes privées par cinq publiques et vous obtiendrez environ 25 méthodes cachées qui ne seront probablement appelées qu'une seule fois par ces méthodes publiques.

C'est ce qu'on appelle l' encapsulation , qui crée une abstraction de contrôle au niveau supérieur. C'est une bonne chose.

Cela signifie que toute personne lisant votre code, quand ils arrivent à la startTheEngine()méthode dans votre code, peut ignorer tous les détails de niveau inférieur tels que openIgnitionModule(), turnDistributorMotor(), sendSparksToSparkPlugs(), injectFuelIntoCylinders(), activateStarterSolenoid(), et toutes les autres fonctions complexes, petits, qui doivent être exécutées afin de faciliter la fonction beaucoup plus grande et plus abstraite de startTheEngine().

Sauf si le problème que vous rencontrez dans votre code concerne directement l'un de ces composants, les responsables du code peuvent passer à autre chose, en ignorant la fonctionnalité encapsulée en bac à sable.

Cela présente également l’avantage supplémentaire de rendre votre code plus facile à tester. . Par exemple, je peux rédiger un scénario de turnAlternatorMotor(int revolutionRate)test et tester ses fonctionnalités de manière totalement indépendante des autres systèmes. S'il y a un problème avec cette fonction et que le résultat n'est pas ce que j'attendais, je sais alors quel est le problème.

Le code qui n'est pas décomposé en composants est beaucoup plus difficile à tester. Soudain, vos responsables ne font que regarder l’abstraction au lieu de pouvoir creuser des composants mesurables.

Mon conseil est de continuer à faire ce que vous faites car votre code évoluera, sera facile à gérer et pourra être utilisé et mis à jour pour les années à venir.

jmort253
la source
3
Vous croyez donc qu'il est bon «d'encapsuler» de mettre la logique à la disposition de toute une classe appelée à un seul endroit?
Steven Jeuris
9
@Steven Jeuris: Tant que ces fonctions sont privées, je ne vois pas de problème avec cela; En fait, je trouve que, comme indiqué dans cette réponse, il est plus facile à lire. Il est beaucoup plus facile de comprendre une fonction publique de haut niveau qui appelle uniquement une série de fonctions privées de bas niveau (bien nommées, comme il se doit). Bien sûr, tout ce code aurait pu être intégré à la fonction de haut niveau elle-même, mais il faudrait alors plus de temps pour le comprendre, car vous devez l'examiner de manière beaucoup plus détaillée au même endroit.
Gablin
2
@gablin: les fonctions privées sont toujours accessibles depuis toute la classe. Dans cet article (apparemment controversé), je discute de la perte de «lisibilité globale» lorsque trop de fonctions sont utilisées. En fait, un principe commun à part que les fonctions ne devraient faire qu’une chose, c’est que vous ne devriez pas en avoir trop. Vous ne gagnez que la «lisibilité locale» en scindant simplement par souci de concision, ce qui peut également être obtenu avec de simples commentaires et des «paragraphes de code». Ok, le code devrait être auto-documenté, mais les commentaires ne sont pas obsolètes!
Steven Jeuris
1
@ Steven - Qu'est-ce qui est plus facile à comprendre? myString.replaceAll(pattern, originalData);ou moi coller toute la méthode replaceAll dans mon code, y compris le tableau de caractères de sauvegarde qui représente l'objet chaîne sous-jacent à chaque fois que j'ai besoin d'utiliser les fonctionnalités d'une chaîne?
Jmort253
7
@ Steven Jeuris: Vous avez raison. Si une classe a trop de fonctions (publiques ou privées), alors peut-être que toute la classe essaie d'en faire trop. Dans ce cas, il peut être préférable de la scinder en plusieurs classes plus petites, comme si vous scindiez une fonction trop volumineuse en plusieurs fonctions plus petites.
Gablin
22

Oui et non. Le principe fondamental est: une méthode doit faire une chose et une seule chose

Il est correct de "diviser" votre méthode en méthodes plus petites. Là encore, ces méthodes devraient être suffisamment générales pour ne pas servir uniquement cette méthode "volumineuse" mais être réutilisables à d’autres endroits. Dans certains cas, une méthode suivra une structure arborescente simple, comme une méthode Initialize qui appelle InitializePlugins, InitializeInterface, etc.

Si vous avez une très grande méthode / classe, c'est généralement un signe que cela en fait trop et que vous devez procéder à une refactorisation pour dissocier le "blob". Peut-être cachent-ils une certaine complexité dans une autre classe sous abstraction et utilisent l'injection de dépendance

Homde
la source
1
Il est agréable de lire que tout le monde ne suit pas aveuglément la règle du «une chose»! ; p Belle réponse!
Steven Jeuris
16
Je divise souvent une grande méthode en méthodes plus petites même si elles ne servent que la méthode la plus grande. La raison de cette division est qu’il devient plus facile à manipuler et à comprendre, au lieu d’avoir un seul gros bloc de code (qui peut ou non être auto - et bien commenté).
Gablin
1
C'est bien aussi dans certains cas où vous ne pouvez vraiment pas éviter une méthode de grande taille. Par exemple, si vous avez une méthode Initialize, appelez InitializeSystems, InitializePlugins, etc. On devrait toujours vérifier soi-même que le refactoring n'est pas nécessaire
Homde
1
L'important dans chaque méthode (ou fonction) est qu'il doit exister une interface conceptuelle claire, c'est un «contrat». Si vous ne pouvez pas cerner cela, il va y avoir un problème et vous avez mal le factoring. (Documenter explicitement le contrat peut être une bonne chose - ou utiliser un outil pour l'imposer, à la Eiffel - mais ce n'est pas aussi important que de l'avoir là en premier lieu.)
Donal Fellows
3
@ gablin: un autre avantage à prendre en compte: lorsque je divise les choses en fonctions plus petites, ne pensez pas "elles ne servent qu'à une autre fonction", pensez "elles ne servent qu'à une autre fonction pour le moment ". Il est arrivé à plusieurs reprises que la re-factorisation de méthodes volumineuses m'a permis de gagner beaucoup de temps lors de l'écriture d'autres méthodes à l'avenir, car les fonctions plus petites étaient plus génériques et réutilisables.
GSto
17

Je pense qu'en général, il vaut mieux pécher par excès de fonctions que par trop peu. Les deux exceptions à cette règle que j'ai constatées dans la pratique concernent les principes DRY et YAGNI . Si vous avez beaucoup de fonctions presque identiques, vous devez les combiner et utiliser des paramètres pour éviter de vous répéter. Vous pouvez également avoir trop de fonctions si vous les avez créées "au cas où" et si elles ne sont pas utilisées. Je ne vois absolument rien de mal à avoir une fonction qui n'est utilisée qu'une fois si elle ajoute de la lisibilité et de la maintenabilité.

Karl Bielefeldt
la source
10

La bonne réponse de jmort253 m'a inspiré à donner la réponse "oui, mais" ...

Avoir beaucoup de petites méthodes privées n'est pas une mauvaise chose, mais faites attention à ce sentiment insignifiant qui vous fait poser une question ici :). Si vous écrivez des tests axés uniquement sur des méthodes privées (soit en les appelant directement, soit en configurant un scénario tel que celui-ci est appelé de manière à pouvoir tester le résultat), vous devriez prendre du recul.

Vous avez peut-être une nouvelle classe avec sa propre interface publique plus ciblée qui tente de s’échapper. À ce stade, vous voudrez peut-être envisager d'extraire une classe pour encapsuler cette partie de la fonctionnalité de votre classe existante qui vit actuellement dans une méthode privée. Désormais, votre classe d'origine peut utiliser votre nouvelle classe comme dépendance et vous pouvez écrire directement des tests plus ciblés sur cette classe.

Pete Hodgson
la source
Je pense que c'est une excellente réponse et une direction que le code pourrait certainement aller. Les meilleures pratiques recommandent d’utiliser le modificateur d’accès le plus restrictif qui vous donne les fonctionnalités dont vous avez besoin. Si les méthodes ne sont pas utilisées en dehors de la classe, private a plus de sens. S'il est plus logique de rendre les méthodes publiques ou de les déplacer dans une autre classe afin de pouvoir les utiliser ailleurs, il est tout à fait possible de le faire également. +1
jmort253
8

Presque tous les projets OO auxquels j'ai adhéré ont souffert de classes trop grandes et de méthodes trop longues.

Lorsque je ressens le besoin d'un commentaire expliquant la section de code suivante, je l'extrais dans une méthode distincte. À long terme, cela raccourcit le code. Ces petites méthodes sont réutilisées plus que prévu initialement. Vous commencez à voir des occasions de rassembler un groupe d'entre eux dans une classe séparée. Bientôt, vous réfléchissez à votre problème à un niveau plus élevé et tout l’effort est beaucoup plus rapide. Les nouvelles fonctionnalités sont plus faciles à écrire, car vous pouvez utiliser ces petites classes que vous avez créées à partir de ces petites méthodes.

Kevin Cline
la source
7

Une classe avec 5 méthodes publiques et 25 méthodes privées ne me semble pas si grande. Assurez-vous simplement que vos classes ont des responsabilités bien définies et ne vous inquiétez pas trop du nombre de méthodes.

Cela dit, ces méthodes privées doivent se concentrer sur un aspect spécifique de la tâche, mais pas de manière "doSomethingPart1", "doSomethingPart2". Vous ne gagnez rien si ces méthodes privées ne sont que des morceaux d'une longue histoire arbitrairement divisée, chacune n'ayant pas de sens en dehors de tout le contexte.

utilisateur281377
la source
+1 pour "Vous ne gagnez rien si ces méthodes privées ne sont que des morceaux d'une longue histoire arbitrairement divisée, chacune n'ayant pas de sens en dehors de tout le contexte."
Mahdi
4

Extrait du Clean Code de R. Martin (p. 176):

Même des concepts aussi fondamentaux que l'élimination de la duplication, l'expressivité du code et le PRS peuvent être poussés trop loin. Afin de réduire la taille de nos classes et méthodes, nous risquons de créer trop de classes et de méthodes minuscules. Donc, cette règle [minimiser le nombre de classes et de méthodes] suggère que nous maintenions également notre fonction et le nombre de classes bas. Le nombre élevé de classes et de méthodes est parfois le résultat d'un dogmatisme inutile.

utilisateur2418306
la source
3

Quelques options:

  1. C'est très bien. Il suffit de nommer les méthodes privées ainsi que vos méthodes publiques. Et nommez bien vos méthodes publiques.

  2. Abstrait certaines de ces méthodes d'assistance dans des classes ou des modules d'assistance. Et assurez-vous que ces classes / modules d’appel sont bien nommés et constituent de solides abstractions en soi.

Yfeldblum
la source
1

Je ne pense pas qu'il y ait jamais un problème de "trop ​​de méthodes privées" si cela semble être le symptôme d'une architecture de code médiocre.

Voici comment j'y pense ... Si l'architecture est bien conçue, il est généralement évident que le code utilisé par X devrait être. Il est acceptable s'il existe quelques exceptions à cela - mais elles doivent être vraiment exceptionnelles, sinon, refactoriser l'architecture pour les gérer en standard.

Si le problème est que lors de l’ouverture de votre classe, elle est pleine de méthodes privées qui divisent de plus gros morceaux en de plus petits morceaux de code, alors c’est peut-être un symptôme de l’architecture manquante. Au lieu de classes et d'architecture, ce domaine de code a été implémenté de manière procédurale dans une classe.

Trouver un équilibre ici dépend du projet et de ses exigences. L'argument opposé à cela est le dicton selon lequel un programmeur junior peut créer la solution en 50 lignes de code qui prennent 50 classes pour un architecte.

Michael Shaw
la source
0

Existe-t-il un trop grand nombre de fonctions / méthodes privées?

Oui.

En Python, le concept de "privé" (tel qu'utilisé par C ++, Java et C #) n'existe pas vraiment.

Il existe une convention d'appellation "en quelque sorte de privé", mais c'est tout.

Privé peut conduire à un code difficile à tester. Il peut également enfreindre le "principe d'ouverture-fermeture" en rendant le code simplement fermé.

Par conséquent, pour les utilisateurs de Python, les fonctions privées n’ont aucune valeur. Il suffit de tout rendre public et d’en finir.

S.Lott
la source
1
Comment peut-il casser le principe d'ouverture fermé? Le code est à la fois ouvert à l’extension et fermé à la modification, que les méthodes publiques aient été scindées en méthodes privées plus petites.
byxor