Refactoring

Publié le 03/09/2019

(Livre bien connu de Martin Fowler)

L'introduction part des principes que

  1. Le code source sera toujours plus souvent lu qu'écrit - Code will be read more often than it will be written.
  2. Que modifier du code existant implique des risques, au travers de l'introduction subtile de nouveaux bugs et de modifications du comportement de certaines fonctionnalités.
  3. En fonction de la base de code existante, une phase de refactoring peut prendre entre plusieurs jours et plusieurs semaines: au plus on creuse, au plus on trouve de choses nécessitant une modification, qui implique à nuoveau de creuser encore, ...

The more you dig, the more stuff you turn up... and the more changes you make. Eventually, you dig yourself into a hole you can't get off. To avoid digging your own grave, refactoring must be done systematically.

Tests (unitaires & intégration)

Construire des tests facilite le travail dans la mesure o√Ļ, quand un bug arrive, les tests qui lui sont associ√©s vont irr√©m√©diablement se planter. Cela indique o√Ļ la modification doit √™tre apport√©e.

Tous ces tests doivent être automatiques, de la même manière que la compilation ou la gestion des dépendances. Ne regardez pas la sortie console, faites juste en sorte qu'un worker vous indique ce qui ne fonctionne plus.

  • Les tests unitaires consistent en des tests localis√©s: quand on re√ßoit un rapport de bug; on commence par faire un test qui expose le bug (ce qui √©vitera ensuite les r√©gressions).
  • Les tests fonctionnels utilisent le syst√®me comme une black box. Id√©alement, ils devraient √™tre √©crits par une √©quipe diff√©rente. Pensez aux conditions sous lesquelles le syst√®me pourrait planter, et concentrez-vous dessus.

Il y a toujours un risque qu'on rate quelque chose, mais il vaut mieux passer un temps raisonnable √† attraper la plupart des bugs, plut√īt que de passer des ann√©es √† leur courir apr√®s.

Conseils de programmation

  • Trop de param√®tres ? Construisez un objet qui les embarque comme champs. Idem quand l'algorithme n'est pas assez clair.
  • Supprimer les param√®tres inutiles.
    • Voire ne pas h√©siter √† refactoriser en ajoutant des param√®tres si deux fonctions font la m√™me chose.
    • Voire, remplacer des param√®tres par des m√©thodes explicites (et √©viter de d√©composer un m√™me objet en plusieurs param√®tres).
  • Ne pas h√©siter √† supprimer des setters ou √† modifier la visibilit√© de certaines m√©thodes (public -> protected -> private).
  • Au besoin, une factory permet aussi de g√©rer un constructeur vers des sous-classes (√©ventuellement en utilisant un brin d'introspection pour aller p√™cher la bonne classe par r√©flexion, sur base d'une cha√ģne de caract√®res). Ou alors passer par des m√©thodes explicites, type Person.newMale() et Person.newFemale().
  • Eviter de retourner des objets qui ne sont pas suffisament typ√©s. On √©vite ainsi de retourner object() plut√īt que MyClass(). Cela √©vite de downcaster la variable retourn√©e par la fonction appelante.
  • Aucun code d'erreur ! Si c'est n√©cessaire, impl√©menter une gestion propre des erreurs (sauf si on finit par avoir tellement de classes qu'un test pourrait suffire...).
  • Dans certains cas, le refactoring fait partie d'une nouvelle fonctionnalit√©. Ele fait alors partie du processus d'√©criture et il est indispensable de se laisser du temps pour impl√©menter les choses proprement.

By refactoring, you can ensure that you fully understand how the program works and should be designed

L'accumulation de décisions d'architecture à moitié comprises peut éventuellement trucider un programme.

Conclusion

Un truc un peu ennuyant quand on lit ce livre, c'est que tous ces exemples "simples", on a parfois l'impression de lire quelques contradictions.

Par exemple, avec la délégation simple: si une classe fait trop de délégations simples et agit comme un middle-man, il faut alors la dégager, parce qu'elle génère un trop grand couplage de classes. Du coup, il ne s'agit pas vraiment d'un livre de recettes à suivre, mais plus d'une concrétisation de l'intuition par l'expérience.

Plus précisément, il s'agit d'une clarification étape par étape de choses que l'expérience nous fait faire intuitivement.