Refactoring
Types de refactoring
Composition des méthodes
La composition des méthodes consiste à limiter la complexité et la taille des méthodes. Cela évite le plus souvent un grand nombre de problèmes. Pour se faire on il existe plusieurs méthodes.
- L'extraction de méthodes
C'est le réfactoring le plus utilisé. Si une méthode est trop longue ou si nous avons besoins de commentaires pour comprendre cette méthode alors il faut en extraire plusieurs méthodes, fragmenter le code.
On passe de :void printOwing(double amount) { printBanner(); //print details System.out.println("name:" + _name); System.out.println("amount" + amount); }
A :void printOwing(double amount) { printBanner(); printDetails(amount); } void printDetails (double amount) { System.out.println ("name:" + _name); System.out.println ("amount" + amount); }
- Les méthodes sur une ligne
On met le corps de la méthode dans le corps de l'appelant et on supprime la méthode.
On passe de :int getRating() { return (moreThanFiveLateDeliveries()) ? 2 : 1; } boolean moreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5; }
A :int getRating() { return (_numberOfLateDeliveries > 5) ? 2 : 1; }
- Les variables temporaires sur une ligne
On remplace toutes les références de cette variable temporaire avec une expression.
On passe de :double basePrice = anOrder.basePrice(); return (basePrice > 1000)
A :return (anOrder.basePrice() > 1000)
- L'explication par les variables : Il suffit d'avoir des variables correctement nommées pour les comprendre
- Suppression des assignations des paramètres
- Diviser les variables temporaires : Une variable temporaire dois être utilisée qu'une seule fois (mise à part dans les boucles). Cela permet ainsi de savoir à quoi elle sert et non d'avoir une variable nommé 'temp' qui sert à chaque fois.
- Remplacer un algorithme : Il peut être nécessaire de rendre un algorithme plus facilement compréhensible.
Responsabilisation des objets
Il ne faut pas oublier qu'en Java tout objet doit avoir une et une seule responsabilité.
Pour pouvoir se conformer à cette règle il existe différents moyens :
- L'extraction de classe : Lorsqu'une classe est trop longue il peut être intéressant de la diviser en deux classes. Cela aussi quand il y a plus d'une responsabilité connues.
- Cacher la délégation *
- Rassembler des classes
Il faut rassembler des classes si une de ces classes est devenue trop petite, c’est souvent le résultat de réfactoring qui déplace les responsabilitées. On rassemble donc deux classes qui ont la même responsabilité.
- Introduction d'extension locale
- Déplacement de méthode : Si une méthode ajoute une responsabilité à une classe ou qu'elle est utilisée par d'autres classes il est mieux de la déplacer.
- Déplacement de champs : c'est le même principe qu'avec les méthodes
Organisation des données
Il faut réussir à organiser ses données de façons cohérentes.
- Changer les associations bidirectionnel en unidirectionnel
- Changer les associations unidirectionnel en bidirectionnel
- Encapsuler les collections
Parfois, vous voyez une série de contrôles conditionnelles dans lequel chaque condition est différente mais l'action qui en résulte est la même. Lorsque vous voyez cela, vous devez utiliser les opérateurs AND et OR pour les regrouper en une seule expression conditionnelle avec un résultat unique.
- Encapsuler les champs : c'est le même principe qu'avec les collections
- Remplacer les Array par des objets
- Changer les références en valeurs
- Changer les valeurs en références
- Remplacer les nombres magiques en constantes symboliques : cela permet ainsi de ne modifier qu'une fois ce nombre magique et de savoir à quoi celui-ci correspond
Simplification des conditions
Il est très important de ne pas multiplier le nombre de conditions dans son code au risque de ne plus s'y retrouver et d'avoir une complexité trop importante.
- Consolider les expression conditionnelle : Créer une méthode avec les différentes conditions.
Avant :
double disabilityAmount() { if (_seniority < 2) return 0; if (_monthsDisabled > 12) return 0; if (_isPartTime) return 0; // compute the disability amount }
Après :
double disabilityAmount() { if (isNotEligableForDisability()) return 0; // compute the disability amount }
- Consolider les fragments de condition dupliqués
- Décomposer les conditions : Si une condition est trop compliqué il est mieux de la décomposer pour la comprendre.
- Introduire des assertions
- Introduire des objets null
- Supprimer les drapeaux de contrôle
- Remplacer les condition par du polymorphisme
Simplification appels méthodes
- Paramétrage de méthode
- Suppression de paramètres : Il n'est pas correct d'avoir trop de paramètres. Il faut peut être ajouter des objets paramétrés
- Ajout de paramètres
- Cacher des méthodes : rendre une méthode privée.
- Introduction d'objet paramétré
Souvent, vous voyez un groupe de paramètres qui tendent à être passé ensemble. Plusieurs méthodes peuvent utiliser ce groupe, que ce soit sur dans une ou plusieurs classes. Un tel groupe peut être remplacé par un objet qui porte l'ensemble de ces données. Ce remaniement est utile car il permet de réduire la taille des listes de paramètres.
- Renommer les méthodes : Le nom de la méthode doit permettre de comprendre sa responsabilité.
- Remplacer constructeur par une factory de méthodes
- Remplacer les codes erreurs par des exceptions : Java gère très bien les exceptions alors pourquoi ne pas le laisser faire?
- Remplacer les exceptions par des tests : Si l'on sait comment corriger une exception alors peut être sait-on la détecter par avance (exemple des nullpointer)
Utilisation de la généralisation
- Extraire une interface
- Extraire une sous-classe
- Extraire une super-classe
- Remonter le corps du constructeur
- Remonter un champs
- Remonter une méthode
- Descendre un champs
- Descendre une méthode