Je travaille sur une base de code assez large. Des centaines de classes, des tonnes de fichiers différents, beaucoup de fonctionnalités, prend plus de 15 minutes pour dérouler une nouvelle copie, etc.
Un gros problème avec une base de code aussi volumineuse est qu'elle a plusieurs méthodes utilitaires et telles qui font la même chose, ou a du code qui n'utilise pas ces méthodes utilitaires quand cela est possible. Et les méthodes utilitaires ne sont pas seulement toutes dans une classe (car ce serait un énorme gâchis).
Je suis assez nouveau dans la base de code, mais le chef d'équipe qui y travaille depuis des années semble avoir le même problème. Cela conduit à beaucoup de code et de duplication de travail, et en tant que tel, quand quelque chose se casse, il est généralement divisé en 4 copies essentiellement du même code
Comment pouvons-nous freiner ce modèle? Comme pour la plupart des grands projets, tout le code n'est pas documenté (même si certains le sont) et tout le code n'est pas ... eh bien, propre. Mais en gros, ce serait vraiment bien si nous pouvions travailler sur l'amélioration de la qualité à cet égard afin qu'à l'avenir nous ayons moins de duplication de code, et des choses comme les fonctions utilitaires soient plus faciles à découvrir.
En outre, les fonctions utilitaires se trouvent généralement dans une classe d'assistance statique, dans une classe d'assistance non statique qui fonctionne sur un seul objet, ou est une méthode statique sur la classe avec laquelle elle "aide" principalement.
J'ai eu une expérience dans l'ajout de fonctions utilitaires en tant que méthodes d'extension (je n'avais pas besoin d'interne de la classe, et cela n'était définitivement requis que dans des scénarios très spécifiques). Cela a eu pour effet d'empêcher d'encombrer la classe primaire et autres, mais ce n'est plus vraiment découvrable à moins que vous ne le sachiez déjà
Réponses:
La réponse simple est que vous ne pouvez vraiment pas empêcher la duplication de code. Vous pouvez cependant "le réparer" à travers un processus incrémentiel répétitif continu difficile qui se résume en deux étapes:
Étape 1. Commencez à écrire des tests sur le code hérité (de préférence en utilisant un cadre de test)
Étape 2. Réécrivez / refactorisez le code qui est dupliqué en utilisant ce que vous avez appris des tests
Vous pouvez utiliser des outils d'analyse statique pour détecter le code dupliqué et pour C #, il existe de nombreux outils qui peuvent le faire pour vous:
Des outils comme celui-ci vous aideront à trouver des points dans le code qui font des choses similaires. Continuez à écrire des tests pour déterminer qu'ils le font vraiment; utilisez les mêmes tests pour rendre le code en double plus simple à utiliser. Cette «refactorisation» peut être effectuée de plusieurs manières et vous pouvez utiliser cette liste pour déterminer la bonne:
De plus, il y a aussi un livre entier sur ce sujet par Michael C. Feathers, Working Effectively with Legacy Code . Il va en profondeur différentes stratégies que vous pouvez prendre pour changer le code au mieux. Il a un "algorithme de changement de code hérité" qui n'est pas loin du processus en deux étapes ci-dessus:
Le livre est une bonne lecture si vous avez affaire à un développement sur fond brun, c'est-à-dire un code hérité qui doit changer.
Dans ce cas
Dans le cas de l'OP, je peux imaginer que le code non testable est causé par un pot de miel pour les "méthodes et astuces utilitaires" qui prennent plusieurs formes:
Prenez note qu'il n'y a rien de mal à cela, mais d'un autre côté, ils sont généralement difficiles à entretenir et à modifier. Les méthodes d'extensions dans .NET sont des méthodes statiques, mais sont également relativement faciles à tester.
Avant de procéder aux refactorisations, parlez-en à votre équipe. Ils doivent être conservés sur la même page que vous avant de poursuivre quoi que ce soit. En effet, si vous refactorisez quelque chose, les chances sont élevées, vous provoquerez des conflits de fusion. Donc, avant de retravailler quelque chose, étudiez-le, dites à votre équipe de travailler sur ces points de code avec prudence pendant un certain temps jusqu'à ce que vous ayez terminé.
Étant donné que l'OP est nouveau dans le code, il y a d'autres choses à faire avant de faire quoi que ce soit:
Bonne chance!
la source
Nous pourrions également essayer de voir le problème sous un autre angle. Au lieu de penser que le problème est la duplication de code, nous pouvons considérer si le problème provient du manque de politiques de réutilisation du code.
J'ai récemment lu le livre Software Engineering with Reusable Components et il contient en effet un ensemble d'idées très intéressantes sur la façon de favoriser la réutilisation du code au niveau de l'organisation.
L'auteur de ce livre, Johannes Sametinger, décrit un ensemble d'obstacles à la réutilisation du code, certains conceptuels, certains techniques. Par exemple:
Selon l'auteur, différents niveaux de réutilisabilité se produisent en fonction de la maturité d'une organisation.
Donc, peut-être, en plus de toutes les suggestions données dans d'autres réponses, vous pourriez travailler sur la conception d'un programme de réutilisabilité, impliquer la gestion, former un groupe de composants responsable de l'identification des composants réutilisables en faisant une analyse de domaine et définir un référentiel de composants réutilisables que d'autres développeurs peuvent facilement interroger et rechercher des solutions cuisinées à leurs problèmes.
la source
Il existe 2 solutions possibles:
Prévention - Essayez d'avoir une documentation aussi bonne que possible. Rendez chaque fonction correctement documentée et facile à rechercher dans toute la documentation. En outre, lors de l'écriture de code, indiquez clairement où le code doit aller, il est donc évident où chercher. Limiter la quantité de code "utilitaire" en est l'un des points clés. Chaque fois que j'entends "laisse faire la classe d'utilité", mes cheveux remontent et mon sang gèle, car c'est évidemment un problème. Ayez toujours un moyen rapide et facile de demander aux gens de connaître la base de code chaque fois qu'une fonctionnalité existe déjà.
Solution - Si la prévention échoue, vous devriez être en mesure de résoudre rapidement et efficacement le morceau de code problématique. Votre processus de développement devrait permettre de corriger rapidement le code en double. Les tests unitaires sont parfaits pour cela, car vous pouvez modifier efficacement le code sans craindre de le casser. Donc, si vous trouvez 2 morceaux de code similaires, les résumer dans une fonction ou une classe devrait être facile avec un peu de refactoring.
Personnellement, je ne pense pas que la prévention soit possible. Plus vous essayez, plus il est problématique de trouver des fonctionnalités déjà existantes.
la source
Je ne pense pas que ce genre de problèmes ait la solution générale. Le code en double ne sera pas créé si les développeurs sont suffisamment disposés à rechercher le code existant. Les développeurs peuvent également résoudre les problèmes sur place s'ils le souhaitent.
Si le langage est C / C ++, la fusion de duplication sera plus facile en raison de la flexibilité de la liaison (on peut appeler n'importe quelle
extern
fonction sans information préalable). Pour Java ou .NET, vous devrez peut-être concevoir des classes d'assistance et / ou des composants utilitaires.Je commence généralement la duplication en supprimant le code existant uniquement si les erreurs majeures proviennent des parties dupliquées.
la source
Il s'agit d'un problème typique d'un projet plus vaste qui a été géré par de nombreux programmeurs, qui ont parfois contribué à la pression de leurs pairs. Il est très très tentant de faire une copie d'une classe et de l'adapter à cette classe spécifique. Cependant, lorsqu'un problème a été trouvé dans la classe d'origine, il doit également être résolu dans ses descendants, ce qui est souvent oublié.
Il existe une solution pour cela et elle s'appelle Generics qui a été introduite dans Java 6. C'est l'équivalent de C ++ appelé Template. Code dont la classe exacte n'est pas encore connue dans une classe générique. Veuillez vérifier Java Generics et vous trouverez des tonnes et des tonnes de documentation pour cela.
Une bonne approche consiste à réécrire du code qui semble être copié / collé à de nombreux endroits en réécrivant le premier que vous devez corriger, à cause d'un certain bug. Réécrivez-le pour utiliser des génériques et écrivez également du code de test très rigoureux.
Assurez-vous que chaque méthode de la classe Generic est invoquée. Vous pouvez également introduire des outils de couverture de code: le code générique doit être entièrement couvert par le code car il sera utilisé à plusieurs endroits.
Écrivez également le code de test, c'est-à-dire en utilisant JUnit ou similaire pour la première classe désignée qui va être utilisée conjointement avec le morceau de code générique.
Commencez à utiliser le code générique pour la deuxième version (la plupart du temps) copiée lorsque tout le code précédent fonctionne et est entièrement testé. Vous verrez qu'il existe des lignes de code spécifiques à cette classe désignée. Vous pouvez appeler ces lignes de code dans une méthode protégée abstraite qui doit être implémentée par la classe dérivée qui utilise la classe de base générique.
Oui, c'est un travail fastidieux, mais au fur et à mesure, il sera de mieux en mieux d'extraire des classes similaires et de le remplacer par quelque chose de très très propre, bien écrit et beaucoup plus facile à entretenir.
J'ai eu une situation similaire où une classe générique a finalement remplacé quelque chose comme 6 ou 7 autres classes presque identiques qui étaient presque toutes identiques mais qui ont été copiées et collées par divers programmeurs sur une période de temps.
Et oui, je suis très favorable à un test automatisé du code. Cela coûtera plus cher au début, mais cela vous fera certainement gagner énormément de temps dans l'ensemble. Et essayez d'obtenir une couverture de code globale d'au moins 80% et 100% pour le code générique.
J'espère que cela vous aidera et bonne chance.
la source
Je vais en fait faire écho à l'opinion la moins populaire ici et à côté
Gangnus
et suggérer que la duplication de code n'est pas toujours nuisible et pourrait parfois être le moindre mal.Si, par exemple, vous me donnez la possibilité d'utiliser:
A) Une bibliothèque d'images stable (immuable) et minuscule, bien testée , qui duplique quelques dizaines de lignes de code mathématique trivial pour les mathématiques vectorielles comme les produits scalaires et les lerps et les pinces, mais est complètement découplée de toute autre chose et construit en une fraction de une seconde.
B) Une bibliothèque d'images instable (changeant rapidement) qui dépend d'une bibliothèque mathématique épique pour éviter les quelques dizaines de lignes de code mentionnées ci-dessus, la bibliothèque mathématique étant instable et recevant constamment de nouvelles mises à jour et modifications, et donc la bibliothèque d'images doit également être reconstruit sinon complètement changé aussi. Il faut 15 minutes pour nettoyer le tout.
... alors évidemment, cela devrait être une évidence pour la plupart des gens que A, et en fait précisément en raison de sa duplication de code mineure, est préférable. L'accent clé que je dois faire est la partie bien testée . Évidemment, il n'y a rien de pire que d'avoir du code dupliqué qui ne fonctionne même pas en premier lieu, à ce moment-là, c'est la duplication de bogues.
Mais il y a aussi le couplage et la stabilité à penser, et une duplication modeste ici et là peut servir de mécanisme de découplage qui augmente également la stabilité (nature immuable) du paquet.
Donc, ma suggestion va être en fait de se concentrer davantage sur les tests et d'essayer de trouver quelque chose de vraiment stable (comme en immuable, trouvant peu de raisons de changer à l'avenir) et fiable dont les dépendances aux sources externes, s'il y en a, sont très stable, en essayant d'éliminer toutes les formes de duplication dans votre base de code. Dans un environnement de grande équipe, ce dernier a tendance à être un objectif peu pratique, sans oublier qu'il peut augmenter le couplage et la quantité de code instable que vous avez dans votre base de code.
la source
N'oubliez pas que la duplication de code n'est pas toujours nuisible. Imaginez: vous avez maintenant une tâche à résoudre dans des modules absolument différents de votre projet. En ce moment, c'est la même tâche.
Il peut y avoir trois raisons à cela:
Certains thèmes autour de cette tâche sont les mêmes pour les deux modules. Dans ce cas, la duplication de code est mauvaise et doit être liquidée. Il serait judicieux de créer une classe ou un module pour prendre en charge ce thème et utiliser ses méthodes dans les deux modules.
La tâche est théorique par rapport à votre projet. Par exemple, c'est de la physique ou des mathématiques, etc. La tâche existe indépendamment sur votre projet. Dans ce cas, la duplication de code est mauvaise et doit également être liquidée. Je créerais une classe spéciale pour de telles fonctions. Et utilisez une telle fonction dans n'importe quel module où vous en avez besoin.
Mais dans d'autres cas, la coïncidence des tâches est une coïncidence temporaire et rien de plus. Il serait dangereux de croire que ces tâches resteront les mêmes lors des modifications du projet en raison du refactoring et même du débogage. Dans ce cas, il serait préférable de créer deux mêmes fonctions / morceaux de code à différents endroits. Et les changements futurs dans l'un d'entre eux ne toucheront pas l'autre.
Et ce 3e cas arrive très souvent. Si vous dupliquez "inconsciemment", c'est surtout pour cette raison - ce n'est pas une vraie duplication!
Alors, essayez de le garder propre quand c'est vraiment nécessaire et n'ayez pas peur de la duplication si ce n'est pas le must.
la source
code duplication is not always harmful
est un mauvais conseil.