Comment éviter les méthodes de colle géantes?

21

Dans mon travail actuel, j'ai été chargé de nettoyer l'ancien code à quelques reprises. Souvent, le code est un labyrinthe et les données qu'il contient sont encore plus enchevêtrées. Je me retrouve à peigner les choses en méthodes agréables, soignées et modulaires. Chaque méthode fait une chose et la fait bien. C'est alors que les choses commencent à aller vers le sud ...

Invariablement, je me retrouve avec une API propre et aucun moyen réel de tout lier ensemble. La solution a été d'écrire une grosse méthode "glue" laide (généralement pleine d'instructions conditionnelles) qui appelle finalement toutes mes méthodes "propres".

La méthode de collage finit généralement par être une version laconique de l'enchevêtrement de code / données que j'essayais de nettoyer. C'est généralement plus lisible, mais c'est toujours ennuyeux.

Comment puis-je éviter de telles méthodes? Est-ce un symptôme des données enchevêtrées ou le reflet de quelque chose que je fais mal?

cmhobbs
la source
3
Les API sont destinées à être utilisées. À partir du désordre emmêlé que vous avez obtenu, vous avez créé une API, puis l'avez à nouveau emmêlé. C'est peut-être juste l'exigence commerciale. Mais vous avez ajouté de la valeur car quelqu'un d'autre peut venir créer facilement une autre fonction de collage à l'aide de votre API. Pas besoin de se tordre la main ...
Aditya MP
1
Sommes-nous même en train de parler d'objets ici ou simplement de fonctions partout?
Erik Reppen
3
Je ne pense pas que ce soit un double de cette question, je parle un peu plus généralement (et à plus grande échelle qu'une seule fonction).
cmhobbs
1
erik - Je parle ici d'objets et de méthodes. J'ai pris quelques dégâts conditionnels et les ai transformés en API. Le problème survient lorsqu'il est temps d'appeler l'API. La première réponse ici peut être exactement ce que je cherche, cependant.
cmhobbs
2
Comment diable est-ce un doublon?
MattDavey

Réponses:

12

Je vais vous donner notre expérience refactoring LedgerSMB. Nous avons pris la décision de faire les choses différemment au début et faisons toujours exactement ce que vous décrivez, mais sans beaucoup de méthodes de collage (nous avons quelques méthodes de collage, mais pas beaucoup).

La vie avec deux bases de code

LedgerSMB a survécu avec deux bases de code pendant environ 5 ans et il en faudra encore plusieurs avant d'éliminer l'ancienne base de code. L'ancienne base de code est une véritable horreur à voir. Mauvaise conception de la base de données, Perl construit comme IS->some_func(\%$some_object);avec du code qui montre exactement pourquoi la métaphore spaghetti est parfois utilisée (des chemins d'exécution serpentant entre les modules et le dos, et entre les langues, sans rime ni raison). La nouvelle base de code évite cela en déplaçant les requêtes db dans des procédures stockées, en ayant un cadre plus propre pour le traitement des demandes, et bien plus encore.

La première chose que nous avons décidé de faire était d'essayer de refactoriser module par module. Cela signifie déplacer toutes les fonctionnalités d'une zone spécifique dans un nouveau module, puis raccorder l'ancien code au nouveau module. Si la nouvelle API est propre, ce n'est pas grave. Si la nouvelle API n'est pas les choses deviennent velues et c'est une invitation à travailler un peu plus fort à la nouvelle API ....

La deuxième chose est qu'il y a de nombreuses fois où le nouveau code doit accéder à la logique de l'ancien code. Cela doit être évité dans la mesure du possible car cela conduit à des méthodes de collage qui sont laides mais on ne peut pas toujours l'éviter. Dans ce cas, les méthodes de collage doivent être minimisées et évitées dans la mesure du possible mais utilisées lorsque cela est nécessaire.

Pour que cela fonctionne, vous devez vous engager à réécrire toutes les fonctionnalités dans une zone spécifique. Si vous pouvez, par exemple, réécrire tous les codes de suivi des informations client à la fois, cela signifie que le code qui appelle cela à partir de l'ancien code n'est pas difficile à travailler, et la répartition vers l'ancien code à partir du nouveau code est minimisée.

La deuxième chose est que si vous avez des abstractions raisonnables à votre place, vous devriez pouvoir choisir le niveau de l'API à appeler et comment le garder propre. Cependant, vous devriez penser à réécrire les parties qui appellent votre API afin qu'elles soient également un peu plus propres.

Il existe de nombreux domaines des outils commerciaux qui sont irréductiblement complexes. Vous ne pouvez pas vous débarrasser de toute complexité. Mais vous pouvez le gérer en vous concentrant sur des API propres qui font spécifiquement ce que vous devez faire et des modules qui utilisent cette API de manière constructive. La colle ne devrait être un dernier recours qu'après avoir considéré que la réécriture du reste du code appelant peut être plus rapide.

Chris Travers
la source
Je pense que vous avez peut-être frappé le clou sur la tête. La raison pour laquelle la colle existe peut être due au code qui appelle l'interface que j'ai créée. Je vais attendre d'autres réponses pour voir si nous manquons quelque chose, mais je crois que celui-ci le résume assez bien.
cmhobbs
1
"des chemins d'exécution serpentant entre les modules et le dos, et entre les langages, sans rime ni raison" - cela me rappelle aussi certaines pratiques modernes d'OO.
user253751
8

Il semble que ce que vous avez fait soit pris dans un désordre embrouillé d'une base de code precurale et créé une belle base de code precural modulaire .

Invariablement, je me retrouve avec une API propre et aucun moyen réel de tout lier ensemble. La solution a été d'écrire une grosse méthode "glue" laide (généralement pleine d'instructions conditionnelles) qui appelle finalement toutes mes méthodes "propres".

Avec le code procédural (même s'il est déguisé en OO), vous allez toujours vous retrouver avec une sorte de flux de travail séquentiel défini quelque part, souvent rempli de branches conditionnelles complexes comme vous le décrivez. Je soupçonne que c'est cette nature procédurale du code qui vous fait sentir que quelque chose ne va pas. Ce n'est pas nécessairement une mauvaise chose, et lorsque vous travaillez avec du code hérité peut être tout à fait inévitable

MattDavey
la source
6

Vous devez nettoyer la méthode de la grande colle laide de la même manière que vous avez nettoyé la base de code d'origine. Divisez-le en méthodes modulaires soignées. Vous avez probablement des groupes de lignes de code qui effectuent certaines tâches en divisant ces lignes en méthodes, si vous partagez certaines variables, vous pouvez envisager de mettre les variables partagées et les nouvelles méthodes dans une classe.

Palissade
la source
2
Tu n'as pas un arbre à colle alors?
Pieter B
3
@PieterB peut-être, mais il est plus facile d'extraire les différentes dépendances lorsque vous avez les différentes tâches dans différentes méthodes. Vous pouvez effectuer une autre passe de refactorisation après avoir extrait les nouvelles méthodes.
Palissade
1

Fondamentalement, vous continuez à ajouter des couches d'abstraction, jusqu'à ce qu'il regarde bien chaque couche prise séparément . La chose paradoxale à propos de l'abstraction est que vous ajoutez de la complexité pour la réduire, car lorsque vous lisez du code abstrait, vous ne vous préoccupez que d'une couche à la fois. Si chaque couche est suffisamment petite pour être facilement comprise, peu importe le nombre de couches sur lesquelles elle repose.

C'est aussi ce qui rend les abstractions difficiles à écrire. Même quelque chose d'aussi simple qu'un crayon est époustouflant si vous essayez de tenir toutes ses couches d'abstraction dans votre tête à la fois. La clé est d'obtenir une couche comme vous le souhaitez, ce que vous avez fait, puis d'oublier toute la complexité qui sous-tend cette couche et de faire la même chose au niveau suivant.

Karl Bielefeldt
la source
0

Il semble que vous refactorisez l'API en pensant simplement à la mise en œuvre de l'API, mais sans penser suffisamment au code qui utilise l'API, c'est-à-dire le "code de colle" dont vous parlez.

Si c'est vrai, vous pouvez essayer de commencer à l'autre bout. Réécrivez d'abord les éléments qui menacent de devenir votre laid code de colle et créez quelques interfaces non encore implémentées qui deviendront votre API dans ce processus. Ne pensez pas trop à la mise en œuvre réelle de cette API pour le moment - c'est OK si vous avez le sentiment que vous pouvez le faire. Et puis seulement réécrire le labyrinthe de code pour se conformer à cette API. Bien sûr, il y aura quelques changements dans l'API et le code de colle dans ce processus, mais cela devrait mieux s'emboîter.

Hans-Peter Störr
la source