Design smell : long method

Posted by Alexandre Heimburger Sun, 23 Mar 2008 13:24:00 GMT

 Quoi de plus désagréable que d’arriver dans un code dans lequel les méthodes font 400 à 500 lignes !

Au delà du fait qu’il faut 2 heures pour arriver à comprendre ce que fait cette méthode, "long method" pose 2 problèmes d’architecture :

  • partage de logique : souvent dans 2 méthodes longues, voire au sein de la même, on retrouve du code dupliqué
  • maintenance : qui peut garantir qu’il se souviendra de ce que font exactement les 400 lignes de codes plusieurs mois après

Lorsqu’on jette un oeil à des frameworks tels que le Zend Framework en PHP ouHibernate en JAVA, il est plaisant de constater que beaucoup de méthodes ne dépassent pas 10 à 20 lignes et que les noms choisis sont explicites. En parcourant le code d’une classe, on comprend instantanément son fonctionnement.

Evidemment personne n’est là pour fixer un nombre maximum de lignes par méthode. Personnellement je pense que le nombre optimal se situe entre 30 et 40.

Comment procéder pour réduire la taille du contenu d’une méthode ?

Le cas le plus simple

La méthode se contente d’appeler une longue série d’instructions, utilisant des variables locales et renvoyant un résultat.
Dans ce cas, le pattern Extract Method qui propose de découper le contenu en sous ensembles de code, suffira à simplifier la méthode. L’attention doit être portée sur le nom 
des "sous-méthodes", afin qu’il soit le plus explicite possible. Attention à leur portées. Dans la majorité des cas, ces méthodes extraites doivent être privées. Inutile d’exposer du code inutilement.

Ma méthode concatène des informations dans une variable avant de la renvoyer

On observe souvent ce genre de choses dans du code manipulant des chaînes de caractères, ou des tableaux. Tout le contenu de la méthode accumule des données.
Une solution est d’adopter le pattern "move accumulation to collecting parameters".

Identifier la variable qui collecte les données, puis extraire des sous-méthodes qui prennent en paramètre cette variable et qui contribuent à son "remplissage".

Ma méthode contient un switch énorme (aussi appelé râteau) pour traiter les données ou appeler d’autres codes

Nicolas le jardinier doit être content. Cependant, nous, développeurs que nous sommes préférons les patterns "command dispatcher" ou "Strategy".
On peut trouver ces "conditional statements" dans des codes d’analyses numériques qui appellent un algorithme en fonction de paramètres d’entrées, ou bien dans des servlets ou des contrôleurs qui traitent les requêtes http avant de les dispatcher.

Que se passe-t-il si on souhaite rajouter une nouvelle logique ? Et bien il faut modifier le switch appelant. On se retrouve alors avec un couplage fort entre les composants.

Les patterns command et strategy, proposent de déléguer à des classes ou des méthodes ces différentes logiques. L’instantiation se fait dynamiquement, par réflexionou via une factory. Rajouter une nouvelle logique revient alors à rajouter une méthode ou une classe, sans toucher à la méthode appelante. Bien utilisé, ce pattern permet d’étendre des fonctionnalités d’un code dont on ne dispose pas des sources.

De tous les design smells, je considère celui-ci comme l’un des pires pour la compréhension, la maintenance, les tests, et la réutilisabilité (je viens d’inventer un mot ?) du code.

Ces quelques recettes devraient permettre de réduire la taille des méthodes de classe, dans la majorité des cas. Je suis preneur de cas particuliers qui ne seraient pas résolus par les patterns évoqués ci-dessus. A vos claviers !