19 votes

Garder la trace des classes d'utilité

J'ai récemment été de plus en plus frustré par un problème que je vois émerger dans la base de code de mes projets.

Je travaille sur un projet Java de grande envergure qui compte plus d'un million de lignes de code. Les interfaces et la structure des classes sont très bien conçues et les ingénieurs qui écrivent le code sont très compétents. Le problème est qu'en essayant de rendre le code plus propre, les gens écrivent des classes utilitaires chaque fois qu'ils ont besoin de réutiliser une fonctionnalité, ce qui fait qu'avec le temps et la croissance du projet, de plus en plus de méthodes utilitaires apparaissent. Cependant, lorsque l'ingénieur suivant a besoin de la même fonctionnalité, il n'a aucun moyen de savoir que quelqu'un a déjà implémenté une classe (ou méthode) utilitaire quelque part dans le code et implémente une autre copie de la fonctionnalité dans une classe différente. Il en résulte une grande duplication du code et un trop grand nombre de classes utilitaires dont les fonctionnalités se chevauchent.

Existe-t-il des outils ou des principes de conception que nous pouvons mettre en œuvre en tant qu'équipe afin d'éviter la duplication et la faible visibilité des classes d'utilité ?

Exemple : L'ingénieur A a 3 endroits où il doit transformer le XML en String. Il écrit donc une classe utilitaire appelée XMLUtil et place un toString(Document) dans celui-ci. L'ingénieur B a plusieurs endroits où il sérialise les documents dans différents formats, y compris les chaînes de caractères, il écrit donc une classe utilitaire appelée SerializationUtil et possède une méthode statique appelée serialize(Document) qui renvoie une chaîne de caractères.

Notez que c'est plus qu'une simple duplication de code car il est tout à fait possible que les 2 implémentations de l'exemple ci-dessus soient différentes (disons que l'une utilise l'API du transformateur et l'autre Xerces2-J) donc cela peut être vu comme un problème de "meilleures pratiques" également...

Mise à jour : Je suppose que je décris mieux l'environnement actuel dans lequel nous évoluons. Nous utilisons Hudson pour le CI, Clover pour la couverture du code et Checkstyle pour l'analyse statique du code. Nous utilisons un développement agile comprenant des entretiens quotidiens et des revues de code (peut-être insuffisantes). Nous définissons toutes nos classes utilitaires dans un .util qui, en raison de sa taille, compte maintenant 13 sous-paquets et environ 60 classes sous la classe Root (.util). Nous utilisons également des bibliothèques tierces telles que la plupart des jars apache commons et certains des jars qui composent Guava.

Je suis certain que nous pouvons réduire de moitié le nombre d'utilitaires si nous confions à quelqu'un la tâche de remanier l'ensemble de ce paquet. Je me demandais s'il existe des outils qui peuvent rendre cette opération moins coûteuse et s'il existe des méthodologies qui peuvent retarder autant que possible la répétition du problème.

9voto

xpmatteo Points 4850

Une bonne solution à ce problème est de commencer à ajouter de l'orientation objet. Pour reprendre votre exemple :

Exemple : l'ingénieur A a 3 endroits où il doit transformer le XML en String. Il écrit donc une classe utilitaire appelée XMLUtil et y place une méthode statique toString(Document).

La solution est d'arrêter d'utiliser les types primitifs ou les types fournis par la JVM (String, Integer, java.util.Date, java.w3c.Document) et de les envelopper dans vos propres classes spécifiques au projet. Votre classe XmlDocument peut alors fournir une méthode toString pratique et d'autres méthodes utilitaires. Votre propre ProjectFooDate peut contenir les méthodes d'analyse et de formatage qui, autrement, se retrouveraient dans diverses classes DateUtils, etc.

De cette façon, l'IDE vous invitera à utiliser vos méthodes utilitaires chaque fois que vous tenterez de faire quelque chose avec un objet.

6voto

Nicolas Bousquet Points 2245

Votre problème est très courant. Et un vrai problème aussi, car il n'y a pas de bonne solution.

Nous sommes dans la même situation ici, enfin je dirais pire, avec 13 millions de ligne de code, du chiffre d'affaire et plus de 800 développeurs travaillant sur le code. Nous discutons souvent du même problème que celui que vous décrivez.

La première idée - que vos développeurs ont déjà utilisée - consiste à remanier le code commun dans certaines classes utilitaires. Notre problème avec cette solution, même avec la programmation en binôme, le mentorat et la discussion, est que nous sommes tout simplement trop nombreux pour que cela soit efficace. En fait, nous nous développons en sous-équipes, les gens partageant les connaissances dans leur sous-équipe, mais les connaissances ne transitent pas entre les sous-équipes. Peut-être avons-nous tort, mais je pense que même la programmation en binôme et les discussions ne peuvent rien faire dans ce cas.

Nous avons également une équipe d'architecture. Cette équipe est chargée de traiter les questions de conception et d'architecture et de créer des utilitaires communs dont nous pourrions avoir besoin. Cette équipe produit en fait quelque chose que nous pourrions appeler un cadre d'entreprise. Oui, c'est un cadre, et parfois il fonctionne bien. Cette équipe est également chargée de promouvoir les meilleures pratiques et de faire prendre conscience de ce qui doit être fait ou non, de ce qui est disponible ou non.

Une bonne conception de l'API de base de Java est l'une des raisons du succès de Java. De bonnes bibliothèques tierces à code source ouvert comptent également pour beaucoup. Même une petite API bien conçue permet d'offrir une abstraction vraiment utile et peut contribuer à réduire considérablement la taille du code. Mais vous savez, créer un cadre et une API publique n'est pas du tout la même chose que de coder une classe utilitaire en deux heures. Cela a un coût très élevé. Une classe utilitaire coûte 2 heures pour le codage initial, peut-être 2 jours avec le débogage et les tests unitaires. Lorsque vous commencez à partager du code commun sur de grands projets/équipes, vous créez vraiment une API. Vous devez alors assurer une documentation parfaite, un code vraiment lisible et facile à maintenir. Lorsque vous publiez une nouvelle version de ce code, vous devez rester rétrocompatible. Vous devez le promouvoir à l'échelle de l'entreprise (ou au moins de l'équipe). De 2 jours pour votre petite classe utilitaire, vous passez à 10 jours, 20 jours ou même 50 jours pour une API à part entière.

Et la conception de votre API n'est peut-être pas très bonne. Ce n'est pas que vos ingénieurs ne soient pas brillants - ils le sont en fait. Mais êtes-vous prêt à les laisser travailler 50 jours sur une petite classe utilitaire qui aide simplement à analyser les nombres d'une manière cohérente pour l'interface utilisateur ? Êtes-vous prêt à les laisser redessiner le tout lorsque vous commencerez à utiliser une interface utilisateur mobile avec des besoins totalement différents ? Avez-vous également remarqué que les ingénieurs les plus brillants du monde créent des API qui ne seront jamais populaires ou qui disparaîtront lentement ? Vous voyez, le premier projet web que nous avons réalisé n'utilisait que des frameworks internes ou pas de framework du tout. Nous avons ensuite ajouté PHP/JSP/ASP. Puis, en Java, nous avons ajouté Struts. Maintenant, JSF est la norme. Et nous envisageons d'utiliser Spring Web Flow, Vaadin ou Lift...

Tout ce que je veux dire, c'est qu'il n'y a pas de bonne solution, les frais généraux augmentent de façon exponentielle avec la taille du code et de l'équipe. Le partage d'une grande base de code limite votre agilité et votre réactivité. Tout changement doit être fait avec soin, vous devez penser à tous les problèmes d'intégration potentiels et tout le monde doit être formé aux nouvelles spécificités et fonctionnalités.

Mais le principal point de productivité dans une entreprise de logiciels n'est pas de gagner 10 ou même 50 lignes de code lors de l'analyse du XML. Un code générique pour ce faire atteindra de toute façon un millier de lignes de code et recréera une API complexe qui sera stratifiée par des classes utilitaires. Lorsqu'un utilisateur crée une classe utilitaire pour analyser le XML, il s'agit d'une bonne abstraction. Il donne un nom à une douzaine ou même une centaine de lignes de code spécialisé. Ce code est utile parce qu'il est spécialisé. L'API commune permet de travailler sur des flux, des URL, des chaînes de caractères, n'importe quoi. Elle possède une fabrique qui permet de choisir l'implémentation de l'analyseur. La classe utilitaire est bonne car elle ne fonctionne qu'avec ce parseur et avec des chaînes de caractères. Et parce que vous n'avez besoin que d'une seule ligne de code pour l'appeler. Mais bien sûr, ce code utilitaire est d'un usage limité. Il fonctionne bien pour cette application mobile, ou pour charger une configuration XML. Et c'est pourquoi le développeur a ajouté la classe utilitaire pour cela en premier lieu.

En conclusion, au lieu d'essayer de consolider le code pour l'ensemble de la base de code, j'envisagerais de répartir la responsabilité du code au fur et à mesure que les équipes se développent :

  • transformer votre grande équipe qui travaille sur un grand projet en petites équipes qui travaillent sur plusieurs sous-projets ;
  • veiller à ce que l'interfaçage soit bon pour minimiser les problèmes d'intégration, mais laisser l'équipe disposer de son propre code ;
  • au sein de ces équipes et des bases de code correspondantes, assurez-vous de disposer des meilleures pratiques. Pas de code dupliqué, bonnes abstractions. Utilisez les API existantes et éprouvées de la communauté. Utilisez la programmation en binôme, une documentation solide sur les API, des wikis... Mais vous devriez vraiment laisser les différentes équipes faire leurs choix, construire leur propre code, même si cela signifie un code dupliqué entre les équipes ou des décisions de conception différentes. Vous savez, si les décisions de conception sont différentes, c'est peut-être parce que les besoins sont différents.

Ce que vous gérez réellement, c'est la complexité. En fin de compte, si vous créez une base de code monolithique, très générique et avancée, vous augmentez le temps nécessaire aux nouveaux arrivants pour se mettre à niveau, vous augmentez le risque que les développeurs n'utilisent pas du tout votre code commun, et vous ralentissez tout le monde parce que tout changement a beaucoup plus de chances de casser la fonctionnalité existante.

4voto

Puce Points 13540

Il existe plusieurs pratiques agiles/XP que vous pouvez utiliser pour résoudre ce problème, par exemple :

  • parler entre eux (par exemple, lors de la réunion quotidienne)
  • programmation en binôme/examen du code

Ensuite, créez, documentez et testez un ou plusieurs projets de bibliothèque d'utilitaires qui peuvent être référencés. Je recommande d'utiliser Maven pour gérer les dépendances et les versions.

3voto

Justin Waugh Points 1901

Vous pourriez envisager de suggérer que toutes les classes utilitaires soient placées dans une structure de paquetage bien organisée comme com.yourcompany.util. . Si les gens sont prêts à bien nommer les sous-paquets et les classes, alors au moins s'ils ont besoin de trouver un utilitaire, ils savent où chercher. Je ne pense pas qu'il existe une solution miracle. La communication est importante. Peut-être que si un développeur envoie un simple courriel au reste de l'équipe de développement lorsqu'il écrit un nouvel utilitaire, cela suffira pour que les gens le remarquent. Ou une page wiki partagée où les gens peuvent les lister/documenter.

1voto

Milimetric Points 7539
  1. Communication au sein de l'équipe (criant "hé, quelqu'un a-t-il un Document toString ?")
  2. Gardez les classes utilitaires au strict minimum et limitez-les à un seul espace de nom.
  3. Pensez toujours : comment puis-je faire cela avec un objet. Dans votre exemple, j'étendrais la classe Document et ajouterais les éléments suivants toString y serialize les méthodes qui s'y rapportent.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X