Aller au contenu

Anti-patrons et Odeurs de Code

Jusqu’à présent, nous avons surtout examiné le code d’un point de vue « terrain vierge », c’est-à-dire que nous avons réfléchi aux solutions pouvant être appliquées lorsqu’on bâtit un logiciel à partir de zéro. En réalité, vous devrez toujours travailler avec du code préexistant, et il est fort probable qu’il soit loin d’être parfait. Dans ce dernier cours, nous allons catégoriser les problèmes fréquents du code existant, comment les détecter et quoi faire pour y remédier.

Conclusion du cours

Dans ce dernier cours, nous plongerons dans les problèmes courants du code, comment les identifier et les résoudre, ainsi que les outils qui peuvent vous aider en cours de route.

Définition d’un anti-patron

  • Semble résoudre un problème
    • Donne l’impression de traiter un problème.
    • A été choisi de façon délibérée.
  • A plus de conséquences négatives que positives. Très souvent…
    • Inefficace pour résoudre le problème d’origine.
    • Contre-productif pour résoudre le problème d’origine.
  • Il existe une meilleure solution reconnue pour traiter le problème.

Pas d’anti-patrons sans patrons

Il existe une distinction subtile entre les anti-patrons et les simples plaintes. Pour qu’un élément soit reconnu comme un anti-patron, il doit aussi exister une façon adéquate et reconnue de mieux résoudre le problème initial :
« Les AntiPatterns qui ne décrivent que la solution négative sont appelés pseudo-AntiPatterns (ou, plus typiquement, plaintes) » – antipatterns.com

Catégories d’anti-patrons

Les anti-patrons, au sens original, se concentraient strictement sur la conception logicielle. Cependant, au fil du temps, le terme s’est élargi pour inclure également les anti-patrons de gestion de projet et d’architecture logicielle.

Pour être complets, voici un aperçu des trois types d’anti-patrons :

  1. Anti-patrons de conception logicielle (ou de développement) : mauvaises solutions de conception orientée objet.
  2. Anti-patrons méthodologiques / de gestion : problèmes de communication ou de répartition des tâches nuisant aux processus de développement logiciel.
  3. Anti-patrons d’architecture : problèmes liés…

Pas vraiment l’opposé.

La notion générale d’anti-patron n’est pas exactement l’opposé des patrons de conception. Ce qui s’en rapproche le plus est la sous-catégorie des anti-patrons de conception logicielle.

Anti-patrons courants

Dans ce qui suit, nous examinons une sélection d’anti-patrons.

Objet Dieu (God Object)

  • Motivation / cause : J’ai déjà une classe qui gère ma logique; j’ajouterai toujours la logique supplémentaire dans cette même classe, ainsi tout reste au même endroit.
  • Anti-patron : Une seule classe tend à contenir toute la logique ou à assumer plus de responsabilités qu’elle ne le devrait. La classe devient énorme et dépend de tout.
  • Patron approprié : Séparation des responsabilités selon des frontières raisonnables, en envisageant des modules supplémentaires lors de l’ajout de fonctionnalités.

Marteau d’or (Golden Hammer)

  • Motivation / cause : Manque de temps, besoin d’accélérer l’implémentation. Maîtrise insuffisante de l’espace des solutions.
  • Anti-patron : S’appuyer obsessivement sur une technologie familière au lieu de rechercher la technologie la mieux adaptée.
  • Patron approprié : Effectuer une analyse des besoins, se renseigner sur toutes les solutions possibles, évaluer leur légitimité individuelle, choisir la plus adéquate.
  • Exemple : Ignorer le cadre des collections et résoudre chaque problème à l’aide de tableaux.

Écoulement de lave (Lava Flow)

  • Motivation / cause : Le code a évolué; certaines fonctionnalités existantes ont changé ou ont été remplacées par de nouvelles.
  • Anti-patron : Ne pas retirer les fonctions héritées du code et les conserver à côté des révisions, « au cas où ».
  • Patron approprié : Supprimer le code mort.

Poltergeist

  • Motivation / cause : Une focalisation excessive sur la séparation des responsabilités, menant à fragmenter les classes au-delà de ce qui est raisonnable pour maintenir la cohésion.
  • Anti-patron : Introduire de nouvelles classes uniquement pour déclencher certaines fonctionnalités ou pour cacher la logique réelle derrière un appel supplémentaire.
  • Patron approprié : Avant de diviser des classes, vérifier si les classes nouvellement créées offrent une valeur ou une contribution minimale.

Obsession des types primitifs (Primitive Obsession)

  • Motivation / cause : Je dois implémenter différents comportements pour divers scénarios.
  • Anti-patron : Ne créons pas de nouvelles classes si on peut utiliser des types primitifs : ainsi 1 signifie que mon client est régulier, 2 qu’il a une carte de rabais, etc.
  • Patron approprié : Utiliser des constantes ou du polymorphisme.

Odeurs de code

Avec les anti-patrons, nous avons examiné des décisions courantes, c’est-à-dire des choix conscients faits pour traiter un problème précis. Souvent, ces anti-patrons ne sont pas directement visibles dans le code.

Néanmoins, les programmeur·euse·s expérimenté·e·s développent souvent un sixième sens pour repérer quelque chose de « louche » dans le code, un peu comme un prédateur qui se hérisse en percevant une odeur.

Capture de mon prédateur domestique. Il a développé un sixième sens pour détecter l’ouverture d’un sac de nourriture pour chats : de très faibles indices lui suffisent pour savoir qu’il y aura de la nourriture.

Définition

Les odeurs de code sont des caractéristiques du code ou des activités laborieuses rencontrées lorsqu’on travaille avec du code, indiquant une faible qualité ou potentiellement des lacunes de conception. Elles n’impliquent pas nécessairement un bogue, mais sont un symptôme de mauvaises pratiques antérieures qui peuvent mener à de la dette technique si elles ne sont pas corrigées.

En quoi les odeurs de code diffèrent-elles des erreurs de programmation ?

Les erreurs de programmation (ou bogues) sont fonctionnelles. Un programme peut n’avoir aucun bogue et tout de même présenter des odeurs de code.

Les odeurs de code ne sont pas intentionnelles, mais le produit d’un manque d’entretien. Elles décrivent des propriétés défavorables du code qui se sont aggravées avec le temps, sans avoir fait l’objet de décisions conscientes.

Comparaison conceptuelle

Les anti-patrons et les odeurs de code ne sont pas entièrement disjoints, mais on peut résumer quelques indicateurs permettant de les distinguer :

Anti-patron Odeur de code
Origine Choix de conception conscient ou habituel, jugé pratique. Négligence, raccourcis ou pratiques locales.
Repérage Pas trivial, souvent abstrait ou dispersé. Facile.
Gravité Inefficace ou nuisible à l’objectif. Problème local pouvant indiquer un anti-patron.
Action Remplacer / refactoriser en utilisant un patron établi. Refactoriser localement, vérifier s’il y a un anti-patron plus large.

Odeurs courantes

Dans ce qui suit, nous examinons une sélection d’odeurs de code courantes.

Code dupliqué

  • Symptôme : Les mêmes lignes de code, ou des lignes très similaires, apparaissent à plusieurs endroits dans la base de code.
  • Illustration : Le code dupliqué est affiché en une même couleur; le code similaire en couleurs similaires :

  • Pourquoi est-ce un problème : Le code dupliqué est néfaste, car il rend la maintenance difficile : lorsqu’on corrige un bogue, on risque d’oublier des endroits nécessitant la même correction.
  • Que faire : Extraire une méthode commune ou une superclasse commune. Si le code n’est pas exactement identique, utiliser des paramètres d’appel.

Méthode trop longue

  • Symptôme : Une méthode est trop longue ou trop imbriquée (la « règle des 20 lignes maximum » est une bonne indication).
  • Pourquoi est-ce un problème : Les blocs de code longs sont difficiles à comprendre. La méthode fait probablement plus que ce qu’elle devrait ou pourrait être simplifiée.
  • Que faire : Simplifier le code, le rendre plus modulaire, par exemple en extrayant des méthodes pour les sous-fonctionnalités.

Chirurgie au fusil de chasse (Shotgun Surgery)

  • Symptôme : Vous souhaitez faire un petit changement, mais vous constatez que de nombreuses modifications implicites sont nécessaires dans beaucoup d’autres classes.
  • Illustration : Un changement initial (cercle bleu à gauche) entraîne implicitement de nombreuses modifications ailleurs dans plusieurs classes.

  • Pourquoi est-ce un problème : Si un changement supposé bénin affecte de nombreuses autres parties de la base de code, cela suggère une faible cohésion.
  • Que faire : Vérifier si ce qui est affecté par le changement (tous les cercles bleus) peut être refactorisé dans une classe additionnelle dédiée.

Envie de fonctionnalités (Feature Envy)

  • Symptôme : Une classe dépend trop des éléments internes d’une autre classe. Par exemple, une méthode qui utilise plus de données d’une autre classe que de la sienne.
  • Illustration : Une classe dépend fortement des fonctionnalités d’une autre classe, par exemple pour des accès de propriétés. Il existe un fort couplage unidirectionnel entre deux classes distinctes.

  • Pourquoi est-ce un problème : Un couplage fort suggère que les frontières entre classes ne sont pas au bon endroit.
  • Que faire : L’envie de fonctionnalités — c’est-à-dire un couplage unidirectionnel spécifique — peut indiquer qu’une classe devrait en absorber une autre.

Instructions switch

  • Symptôme : Les instructions switch indiquent que vous avez manqué une occasion d’utiliser le polymorphisme. Possiblement présence d’une classe dieu ou d’énumérations qui devraient être décomposées.
  • Exemple :
/**
 * Example taken from Martin Fowler's "Refactoring", page 205.
 * The function computes the speed of different birds.
 */
double getSpeed() {
    switch (_type) {
        case EUROPEAN:
            return getBaseSpeed();
        case AFRICAN:
            return getBaseSpeed() - getLoadFactor() *
                _numberOfCoconuts;
        case NORWEGIAN_BLUE:
            return (_isNailed) ? 0 : getBaseSpeed(_voltage);
    }
    throw new RuntimeException ("Should be unreachable");
}
  • Pourquoi est-ce un problème : Une logique qui devrait être répartie entre différents objets se retrouve concentrée dans une longue série de conditions. C’est difficile à lire et encore plus difficile à maintenir. Chaque fois qu’un nouvel oiseau apparaît, l’instruction switch devra être allongée.
  • Que faire : Utiliser le polymorphisme, placer la logique propre à chaque oiseau dans des sous-classes individuelles :
---
title: Polymorphism
---
classDiagram
    class Bird {
        +getSpeed()
    }

    class European {
        +getSpeed()
    }

    class African {
        +getSpeed()
    }

    class NorwegianBlue {
        +getSpeed()
    }

    Bird <|-- European
    Bird <|-- African
    Bird <|-- NorwegianBlue

Exemple de code et diagramme tirés de Martin Fowler, Refactoring, pages 205–207.

Valeurs magiques

  • Symptôme : Des valeurs primitives sont utilisées pour encoder une signification.
  • Exemple :

// customerType should not be a primitive, there is no link
// between the magic value (1, 2, 3) and the semantic it carries...
public double calculateFinalPrice(double originalPrice, int customerType) {
    // Magic values used directly in the code
    if (customerType == 1) { // 1 = Regular
        return originalPrice;
    } else if (customerType == 2) { // 2 = Premium
        return originalPrice * 0.9; // 10% discount
    } else if (customerType == 3) { // 3 = VIP
        return originalPrice * 0.8; // 20% discount
    } else {
        return originalPrice;
    }
}
* Pourquoi est-ce un problème : Il n’existe aucun lien explicite entre la valeur primitive et la signification qu’elle porte. Le code devient difficile à comprendre et à maintenir. * Que faire : Remplacer par une énumération (enum) ou des constantes.

Commentaires trop longs

  • Symptôme : En général, les commentaires sont souhaitables. Mais de longs blocs de commentaires peuvent être un signe de problème.
  • Exemple :
public double calculateProjectileRange(double velocity, double angleInDegrees, double height) {
    // The following formula calculates the horizontal range of a projectile
    // launched from a certain height with an initial velocity at a given angle.
    //
    // Formula explanation:
    // R = (v * cosθ / g) * [v * sinθ + sqrt((v * sinθ)^2 + 2gh)]
    //
    // where:
    //   v = initial velocity
    //   θ = launch angle
    //   g = gravitational acceleration (9.81 m/s^2)
    //   h = initial height
    //
    // This formula accounts for the projectile being launched from above the ground
    // (h > 0). If h = 0, it simplifies to the standard range formula:
    //   R = (v^2 * sin(2θ)) / g
    //
    // Warning: This formula assumes no air resistance, a flat Earth, and constant gravity.
    // It’s not valid for extremely high velocities or very large distances.

    double g = 9.81;
    double angleInRadians = Math.toRadians(angleInDegrees);

    return (velocity * Math.cos(angleInRadians) / g) *
           (velocity * Math.sin(angleInRadians) +
            Math.sqrt(Math.pow(velocity * Math.sin(angleInRadians), 2) + 2 * g * height));
}
  • Pourquoi est-ce un problème : Si beaucoup d’explications sont nécessaires, il y a de fortes chances que le code soit trop complexe et difficile à comprendre.
  • Que faire : Vérifier d’abord si vous pouvez décrire clairement ce qui se passe en termes plus simples. Si ce n’est pas possible, revoir le code lui-même et itérer.

Dans cet exemple, le code n’a pas beaucoup de potentiel de simplification, puisqu’il s’agit simplement de la formule exacte de la portée d’un projectile. À la limite, on pourrait décomposer le code en termes partiels, chacun avec un nom de fonction intuitif.

Classe paresseuse (Lazy class)

  • Symptôme : Présence de multiples méthodes presque vides qui semblent seulement déléguer des appels de fonctions directement à d’autres classes.
  • Exemple :

class Poltergeist {

    public static void doSomething() {
        // The only thing this class does, is to
        // forward a call elsewhere...
        Foo.doSomething();
    }
}
* Pourquoi est-ce un problème : La classe laisse toujours une empreinte dans la base de code, mais n’apporte aucune valeur. Le graphe des appels est obscurci. * Que faire : Vérifier si la classe est en réalité un « Poltergeist ». Si c’est le cas, supprimer ces proxies et appeler directement ce à quoi ils délèguent.

Détection automatique des odeurs

Nous avons vu précédemment que les « odeurs de code » ne sont que des indicateurs et nécessitent toujours une intuition humaine pour décider si un refactoring est approprié.
En plus de ces indications générales, il existe quelques métriques plus faciles pour évaluer la qualité du code. Ce ne sont pas des anti-patrons ou des odeurs, mais elles fournissent tout de même des retours utiles.

Checkstyle

Pour la validation syntaxique, les linters ne construisent généralement pas d’AST et raisonnent directement sur la structure des tokens extraits. Les linters se concentrent souvent sur l’application des conventions visuelles du code :

  • Indentation
  • Placement des accolades
  • Sauts de ligne
  • Nommage des variables
  • ...

Un exemple est Checkstyle, que nous avons déjà vu en classe. Checkstyle utilise un fichier de configuration pour s’assurer que tous les membres de l’équipe suivent les mêmes conventions visuelles.
Pourquoi est-ce utile ?

  • Le code est plus facile à lire si tout le monde suit la même convention.
  • Moins de conflits lorsqu’on travaille avec un VCS, par exemple Git.
Pourquoi est-ce pertinent dans le contexte des odeurs de code ?

Un code non formaté est un signe de négligence.

Plugin Checkstyle pour IDE

Complexité

Il existe souvent plusieurs façons de coder la même fonctionnalité.

  • Dans le meilleur des cas, le comportement d’une solution complexe est le même que celui d’une solution plus simple.
  • Mais un code complexe est difficile à comprendre, à manipuler et à maintenir.
  • Votre code ne doit pas seulement être correct, il doit aussi être aussi simple que possible.
Pourquoi est-ce pertinent dans le contexte des odeurs de code ?

Détecter une complexité élevée prolonge la détection des méthodes trop longues. C’est un indicateur qu’une méthode ou une classe gère trop de choses.

Complexité cyclomatique

La complexité cyclomatique donne une idée de la complexité de votre code et fournit des indications pour le simplifier.

Plus concrètement, pour déterminer la métrique de complexité cyclomatique, on utilise l’AST pour créer une représentation en graphe du flux de contrôle du programme :

  • Les structures de contrôle dans l’AST deviennent des nœuds du graphe : début et fin de fonction, if, else, boucles, blocs de code.
  • Les chemins d’exécution entre structures de contrôle deviennent des arêtes du graphe.

À partir des propriétés du graphe de flux de contrôle, on calcule une métrique de complexité par méthode :

Il n’existe pas de plugin Maven dédié, mais il existe un plugin IDE pour un retour visuel instantané sur la complexité, et Checkstyle (pour lequel il existe un plugin Maven) peut être configuré pour évaluer la complexité cyclomatique.
(Non couvert dans ce cours.)

Spotbugs

Spotbugs (anciennement FindBugs) est un analyseur d’AST surpuissant qui recherche systématiquement dans votre code des bogues potentiels ou même des constructions de code susceptibles de provoquer des bogues.

  • Exemple : si vous avez un getter qui retourne un objet mutable, FindBugs vous en informera.

Plugin Spotbugs pour IDE

Spotbugs est disponible comme plugin IDE.
Après l’installation, vous pouvez accéder à un nouveau menu SpotBugs dans la barre de base pour analyser votre projet ou un fichier spécifique à la recherche de bogues potentiels.

Matrice de dépendances

  • Les IDE ne se contentent pas de gérer des lignes de code individuelles, ils peuvent également fournir des informations sur la macro-structure de votre code.
  • Un exemple est la matrice de dépendances :

Ouvrir avec : "Code -> Complexity Matrix". Cliquez ensuite sur une position et lisez : "vert_ dépend de _jaune".

  • Chaque ligne et colonne représente une classe ; chaque cellule indique combien de fois une classe appelle ou référence son homologue.
  • La matrice est automatiquement triée de bas en haut : les classes en bas sont les plus sollicitées, celles en haut le sont le moins.
  • La matrice fournit également un aperçu des constructions suspectes, par exemple les références cycliques, représentées par des carrés rouges.

Utiliser la matrice pour vérifier les patrons architecturaux, par exemple MVC

La matrice de dépendances est un moyen efficace d’analyser si un projet respecte le patron MVC :
Le modèle (Model) ne traite que des données et ne doit jamais appeler le contrôleur (Controller) ni la vue (View).
Le paquet Model doit être le plus bas dans la matrice.
Le contrôleur modifie l’état du modèle, mais ne se soucie pas de la représentation. Il a moins d’usages que le modèle, mais plus que la vue (personne n’appelle la vue). Les classes du paquet Controller doivent se situer entre le modèle et la vue dans la matrice.

PMD

Pour les vraiment (vraiment, vraiment) courageux. Découvrez PMD (Project Meets Deadline).

  • Il n’est pas facile d’écrire du code sans aucune remarque de PMD. (Attention aux faux positifs !)
  • Mais vous pouvez être sûr que votre code s’améliorera considérablement en essayant (et vous apprendrez beaucoup).

Plugin PMD pour IDE

PMD est disponible comme plugin IDE. Après l’installation, vous pouvez exécuter l’analyse statique du code via Tools -> PMD -> Run predefined -> All.

Complexité, Spotbugs et PMD sont compatibles Maven

Voir les notes du cours INF2050 pour une configuration de plugin Maven de ces outils, afin d’auditer automatiquement la qualité du code dans le cadre du processus de build.

Spotbugs

Spotbugs (anciennement FindBugs) est un analyseur d’AST très puissant qui recherche systématiquement dans votre code des bogues potentiels ou des constructions susceptibles de provoquer des bogues.

  • Exemple : si vous avez un getter qui retourne un objet mutable, FindBugs vous le signalera.

Plugin Spotbugs pour IDE

Spotbugs est disponible comme plugin IDE.
Après installation, un nouveau menu SpotBugs apparaît dans la barre de base, vous permettant d’analyser votre projet ou un fichier spécifique à la recherche de bogues potentiels.

Matrice de dépendances

  • Les IDE ne se limitent pas à la gestion des lignes de code individuelles ; ils fournissent également des informations sur la macro-structure de votre code.
  • Un exemple est la matrice de dépendances :

Ouvrir avec : "Code -> Complexity Matrix". Cliquez sur une position et lisez : "vert_ dépend de _jaune".

  • Chaque ligne et colonne représente une classe ; chaque cellule indique combien de fois une classe appelle ou référence son homologue.
  • La matrice est triée automatiquement de bas en haut : les classes en bas sont les plus utilisées, celles en haut le moins.
  • La matrice fournit aussi un aperçu des constructions suspectes, comme les références cycliques, représentées par des carrés rouges.

Utiliser la matrice pour vérifier les patrons architecturaux, par exemple MVC

La matrice de dépendances est un outil efficace pour vérifier si un projet respecte le patron MVC :
Le modèle (Model) ne traite que des données et ne doit jamais appeler le contrôleur (Controller) ou la vue (View).
Le paquet Model doit être en bas de la matrice.
Le contrôleur modifie l’état du modèle, mais ne se soucie pas de la représentation. Il est moins utilisé que le modèle, mais plus que la vue (personne n’appelle la vue). Les classes du paquet Controller doivent se situer entre le modèle et la vue dans la matrice.

PMD

Pour les courageux (vraiment très courageux), découvrez PMD (Project Meets Deadline).

  • Il n’est pas facile d’écrire du code sans aucune remarque de PMD (attention aux faux positifs !).
  • Mais vous pouvez être sûr que votre code s’améliorera beaucoup en essayant (et vous apprendrez énormément).

Plugin PMD pour IDE

PMD est disponible comme plugin IDE.
Après installation, vous pouvez lancer l’analyse statique via Tools -> PMD -> Run predefined -> All.

Complexité, Spotbugs et PMD sont compatibles Maven

Voir les notes du cours INF2050 pour la configuration des plugins Maven de ces outils, afin d’auditer automatiquement la qualité du code dans le processus de build.

Support au refactoring

Vous n’avez pas besoin d’effectuer toutes les modifications de refactoring manuellement. La plupart des IDE proposent un menu de refactoring complet, véritable couteau suisse pour les scénarios les plus courants.

Ne pas refactoriser aveuglément.

Il est bien sûr nécessaire d’identifier d’abord où le refactoring est pertinent ! Mais suivre les odeurs de code et les indications de Checkstyle/PMD/complexité est un bon point de départ.

Modification des signatures

Similaire au renommage, modifier la signature d’une méthode, par exemple pour ajouter un paramètre d’entrée.

  • Vous devez modifier de manière cohérente tous les appels à la méthode, sinon votre code ne compilera pas.
  • IntelliJ peut vous aider :
    • Modifier uniquement la définition de la signature (le code générera des erreurs)
    • Cliquer sur l’annotation @R dans le panneau latéral gauche
    • Ajouter une valeur par défaut pour le nouveau paramètre dans tous les appels existants
  • Vous pouvez également utiliser la boîte de dialogue dédiée au refactoring de signature avec Command (⌘) + F6 (et Fn sur les portables), qui offre des fonctionnalités supplémentaires, par exemple :
    • Changer l’ordre des paramètres
    • Surcharger les méthodes

Extraction de méthode

  • Souvent, vous avez quelques lignes de code qui accomplissent parfaitement une tâche, mais vous souhaitez les rendre accessibles comme méthode dédiée (appelée là où le code se trouve actuellement).
    • Exemple :
/**
 * Creates an interleaved true, false, true, false... array and prints the outcome.
 * @param length as the total amount of values to be placed in the result array.
 */
void foo(int length) {

  // initialize a new all-false boolean array
  boolean[] myArray = new boolean[length];

  // convert every value at an even position to true:
  for (int i = 0; i < myArray.length; i++) {
    if (i % 2 == 0) {
      myArray[i] = true;
    }
  }

  // print the array content
  for (int i = 0; i < myArray.length; i++) {
    System.out.println(myArray[i]);
  }
}
  • Pourrait être refactorisé en :

/**
 * Creates an interleaved true, false, true, false... array and prints the outcome.
 * @param length as the total amount of values to be placed in the result array.
 */
void foo(int length) {

  // initialize a new all-false boolean array
  boolean[] myArray = new boolean[length];

  setEveryEvenPositionTrue(myArray);

  // print the array content
  for (int i = 0; i < myArray.length; i++) {
    System.out.println(myArray[i]);
  }
}

/**
 * Sets every even position of a provided array to true.
 * @param myArray the array to be modified.
 */
private static void setEveryEvenPositionTrue(boolean[] myArray) {
  // convert every value at an even position to true:
  for (int i = 0; i < myArray.length; i++) {
    if (i % 2 == 0) {
      myArray[i] = true;
    }
  }
}
* Vous pouvez éditer manuellement le code pour créer une nouvelle méthode, copier-coller les lignes sélectionnées, puis insérer un appel à la méthode à l’endroit original... * Ou vous pouvez simplement utiliser un raccourci IntelliJ : Option (⌥) + Command (⌘) + M

Il existe également une opération inverse : Inline method. L’opération inverse remplace un appel à une méthode par le code de la méthode.
Sélectionnez l’appel de méthode et appuyez sur Option (⌥) + Command + N.

Valeurs magiques vers constantes

  • Codifier des valeurs magiques est considéré comme une mauvaise pratique, même si la variable apparaît une seule fois.
  • Sélectionnez la variable et appuyez sur : Option (⌥) + Command (⌘) + C

Champs vers paramètres de méthode

  • Souvent, certaines valeurs dans une méthode devraient être injectées depuis l’extérieur.
  • Vous pouvez marquer les paramètres et appuyer sur Option (⌥) + Command (⌘) + P
  • Exemple :
/**
 * Creates an array and calls toto on it.
 * @param length as the size of the array to create before passing it to toto.
 */
void foo(int length) {

  // initialize a new all-false boolean array
  boolean[] myArray = new boolean[length];
  toto(myArray);
}
  • Pourrait être refactorisé en :
/**
 * Receives an array and calls toto on it.
 * @param myArray as the array to call toto on.
 */
void foo(boolean[] myArray) {
  toto(myArray);
}

Expression vers variable

  • Les expressions longues peuvent être difficiles à comprendre :
if(i< 3984574&&i%2==true||i ==42){
...
}
  • Vous pouvez remplacer des parties de l’expression par des variables nommées avec Option (⌥) + Command (⌘) + v :
boolean iSmallerMaxValue = i < 3984574;
int correctAnswer = 42;
boolean iIsEven = i % 2 == true;
if(iSmallerMaxValue && iIsEven || i ==correctAnswer){
...
}

Littérature

Pour les esprits curieux, quelques inspirations et lectures complémentaires :