Méthodes et classes statiques (ou partagées) : Une règle du pouce

Publié par Olivier Dugas le lundi 7 juillet 2014 à 09:00

Avez-vous déjà lu ou même écrit des méthodes statiques dans un projet pour votre entreprise? Certains l'ont sûrement fait, puisqu'il nous arrive fréquemment d'en voir lorsque nous plongeons dans le code d'entreprises que nous visitons. La question du jour est : Croyez-vous que c'est une bonne pratique?

Avant de répondre à cette question, précisons que les méthodes statiques et partagées sont la même chose. "Static" est le terme employé par Java et C#.Net, alors que VB.Net utilise plutôt le terme "Shared". Par conséquent, les deux doivent être considérés comme interchangeable dans cette publication.

Voici ce que MSDN a à dire à propos des méthodes et classes partagées :

Utilisez une classe statique comme une unité d'organisation pour les méthodes qui ne sont associées à aucun objet particulier. Par ailleurs, une classe statique peut simplifier et accélérer votre implémentation, car vous n'êtes pas obligé de créer un objet pour appeler ses méthodes.

 

Voici un résumé des avantages que les gens nous disent régulièrement à propos des méthodes partagées :

  • Elles sont de bonnes assistantes dans des classes utilitaires
  • Elles sont plus concises à utiliser que le code qui crée une instance sans utilité par la suite dans la classe appelante
  • Elles sont efficaces pour de petits projets sans besoins architecturaux et sans maintenance future envisagée.
  • Il n'y a pas de raison de créer d'instance lorsqu'une classe n'a besoin d'aucun état.

Il y a des avantages évidents, mais de façon générale, nous ne recommandons pas son utilisation car les désavantages représentens une dette ne valant pas les bénéfices.

Polymorphisme

Supposons que nous avons une méthode partagée dans un utilitaire quelconque qui vit nonchalamment dans son coin. Soudainement, nous devons altérer légèrement la fonctionnalité. La plus grande partie de la fonctionnalité est la même, mais nous devons néanmoins changer quelques petites parti La technique normalement employée dans une situation comme celle-ci est de faire de l’héritage, mais il n’est pas possible de procéder ainsi avec les méthodes statiques.  Il n’y a pas de manière élégante de procéder ici, ce qui nous contraindrait d’ajouter une branche conditionnelle (un if). Cela provoquera l’augmentation de la complexité du code et, par extension, l’accumulation de la dette technique.

Le choix des noms

Oh, regardez, une méthode partagée très utile. Oh, en voilà une autre qui joue un rôle semblable, à peu de choses près. Rassemblons-les en une seule classe statique. Comment nommer cette classe? Je ne sais pas trop quel nom lui donner, mais les deux méthodes font des choses en lien à la validation de chaîne de requêtes HTML, alors je vais l'appeler "ValidationHelper"!

Les classes statiques tendent à être difficiles à nommer et, pour cette raison, elles finissent inévitablement par recevoir un nom avec un préfixe générique (tel que Validation) joint à une terminaison horrible comme "Helper", "Manager" ou "Utils". Sentez-vous la fumée? Voici une vision de l'avenir : "Tiens, j'ai créé une nouvelle méthode statique qui valide l'état d'une valeur envoyée sur le réseau. Je me demande où je pourrais bien déposer cette méthode..."

Parce que les éléments partagés sont difficiles à nommer, ceux-ci risquent fort de recevoir un nom générique. De nouvelles fonctionnalités s'y grefferont avec le temps. Ultimement, ces classes deviendront des amalgames de méthodes non reliées, et les paramètres optionnels (ou des méthodes au nom identique mais des paramètres différents) vont commencer à ramper dans les méthodes statiques qui s'y logent. Rappelons au passage qu’un mauvais nom mène souvent à une violation du SRP (Single Responsibility Principle).

Tests unitaires

Il peut être complexe d'écrire et de maintenir des tests unitaires de classes qui utilise des éléments statiques. Bien entendu, nous excluons les classes statiques comme Math, puisque ce sont des outils standards des langages. Rien ne sert de s'isoler des outils statiques d'un langage de programmation. Également, il y a parfois des cas où l’utilisation de classes statiques pour faire de la composition peut être intéressant. Et dans le cas de la composition, nous atteignons souvent mieux l’objectif de “documentation” par nos tests lorsqu’ils ne sont pas parfaitement unitaires. Par contre, nous ne devrions pas dépendre de classes statiques que nous (ou quelqu'un d'autre) pourrait possiblement changer un jour.

Il n'y a aucune manière élégante d'isoler une classe des méthodes statiques dont celle-ci fait usage. Puisque nous ne pouvons faire d'interfaces implémentées par des classes statiques, vous ne pouvez pas les remplacer par des mocks. Également, imaginez que cette classe statique crée et utilise trois classes et appelle l'interface graphique ou la base de donnée. Un simple test unitaire ne pourra être rapide, ne sera pas unitaire et risque d'échouer aléatoirement en fonction de l'orientation du vent. Par la bande, nous vous invitons à lire l'article du blog de Martin Fowler au sujet des tests non-déterministes.

États globaux

Si vous devez absolument avoir des classes partagées dans vos projets (évitez cette situation autant que possible), assurez vous au moins de ne pas maintenir d'attributs partagés à l'intérieur de cette classe. Cela signifie que vous avez des variables globales, ce qui est un véritable cauchemar à déboguer. Et ne vous en faites pas, vous aurez à déboguer, puisque vous n'avez pas de filet de sécurité fait de tests unitaires mais aussi parce que la complexité augmente exponentiellement avec les états globaux. De surcroît, le couplage induit par cette situation tue votre architecture telle un coup de tronçonneuse dans la colonne vertébrale de votre projet. Et avez-vous déjà souhaités porter votre application dans un nuage (Cloud Computing)? Où même d'espérer une meilleure performance de votre système? Il serait bien plus simple de dépôser cette tâche dans la liste des choses-impossibles-à-faire, car les états globaux empêche carrément la parallélisation. 

Une règle du pouce

Maintenant que nous avons vu comment les éléments statiques sont souvent source de bien des maux, comment faire pour s'en débarasser dans cet énorme projet d'entreprise? La classe statique est déjà utilisée par plus de 9000 classes...

Une possibilité est d'utiliser un singleton, mais sous une variante unitairement testable.Nous savons que les singletons présentent les mêmes problèmes que les méthodes statiques. Malgré cela, s'il est impossible de faire la transition vers une classe régulière en une seule bouchée, le singleton testable est une option correcte. 

Une alternative que nous jugeons plus intéressante serait de créer une classe non-statique Adapter qui se charge de transférer les appels aux méthodes statiques. Progressivement, vous pouvez modifier les utilisateurs des méthodes statiques pour qu'ils utilisent plutôt cet Adapter. Il sera ainsi possible de créer un mock de ce dernier dans les tests pour les rendre unitaires. Ultimement, lorsque seule la classe Wrapper fera les appels vers les méthodes statiques, vous pourrez supprimer les méthodes statiques et les réimplémenter dans la classe Adapter.

La tâche peut être lourde. Pour éviter d'échouer ou de se sentir décourager par le travail à faire, il est préférable d'y aller par petites itérations. Ne faites pas la modification en un élan pour ensuite essayer de refaire passer tous les tests qui échoueront. D'autres ont fait cette erreur avant vous.

blog comments powered by Disqus

0 Comments:

Post a comment

Comments have been closed for this post.