Simplification de code

1. Méthode composée

Le patron "Méthode composée" (Composed Method) permet de produire des méthodes qui communiquent efficacement ce qu’elles font (leur comportement) et comment elles le font.

Divide your program into methods that perform one identifiable task. Keep all of the operations in a method at the same level of abstraction.

This will naturally result in programs with many small methods, each a few lines long.»

— Kent Beck

Une méthode composée se compose d’appels à des méthodes bien nommées qui se situent toutes au même niveau de détail. Suivez les instructions ci-dessous pour simplifier la méthode add()

Listing 1. La classe ArrayList (Kerievsky:2004)
package fr.unantes.sce.refactorings;

public class ArrayList {
    private Object[] elements = new Object[10];
    private boolean readOnly;
    private int size = 0;

    public void add(Object child) {
        if (!readOnly) {
            int newSize = size + 1;
            if (newSize > elements.length) {
                Object[] newElements = new Object[elements.length + 10];
                System.arraycopy(elements, 0, newElements, 0, size);
                elements = newElements;
            }
            elements[size] = child;
            size++;
        }
    }

    public void setReadOnly(boolean readOnly) {
        this.readOnly = readOnly;
    }

    public boolean isReadOnly() {
        return readOnly;
    }
}

1.1. Exercice : gardes

  1. Transformez la vérification de la variable readOnly en une garde.

1.2. Exercice : méthode addElement()

  1. Appliquez l’opération de refactoring "Extraction de méthode" aux lignes 18-19 et créez la méthode void addElement(object).

1.3. Exercice : variable explicative

  1. Appliquez l’opération de refactoring "Extraire une constante" pour remplacer le nombre magique "10" en introduisant une "variable explicative" appelée GROWTH_INCREMENT.

1.4. Exercice méthode atCapacity()

  1. Appliquez l’opération "Inline variable" à newSize, puis appliquez à nouveau l’opération "Extraire méthode" au code qui vérifie si le tableau d’éléments a atteint sa capacité et doit être agrandi, et créez la méthode atCapacity().

1.5. Exercice méthode grow()

  1. Enfin, appliquez l’opération de refactoring "Extraire une méthode" au code qui augmente la taille du tableau d’éléments, en créant la méthode grow().

2. Enchaînement de constructeurs

En Java, les classes peuvent avoir plusieurs constructeurs : c’est normal, car il peut y avoir différentes façons d’instancier les objets d’une même classe. Cependant, lorsque le code est dupliqué dans deux constructeurs ou plus, des problèmes de maintenance se posent.

Considérons les trois constructeurs de la classe Loan [Kerievsky:2004], présentés dans le Listing Listing 2, dont le code est dupliqué. Nous allons utiliser l’opération de refactoring appelée "Enchaînement de constructeurs", dont le but est de supprimer la duplication dans les constructeurs et les faire s’appeler entre eux. Tout d’abord, nous analysons ces constructeurs pour déterminer lequel est le "constructeur qui fait tout", celui qui gère tous les détails de la construction.

Il semble que ce soit le constructeur 3, car on peut faire en sorte que les constructeurs 1 et 2 appellent le 3 avec un minimum de travail.

Listing 2. Les constructeurs de la classe Loan
public Loan(float notional, float outstanding, int rating, Date expiry) {
   this.strategy = new TermROC();
   this.notional = notional;
   this.outstanding = outstanding;
   this.rating = rating;
   this.expiry = expiry;
}

public Loan(float notional, float outstanding, int rating, Date expiry, Date maturity) {
   this.strategy = new RevolvingTermROC();
   this.notional = notional;
   this.outstanding = outstanding;
   this.rating = rating;
   this.expiry = expiry;
   this.maturity = maturity;
}

public Loan(CapitalStrategy strategy, float notional, float outstanding, int rating, Date expiry, Date maturity) {
   this.strategy = strategy;
   this.notional = notional;
   this.outstanding = outstanding;
   this.rating = rating;
   this.expiry = expiry;
   this.maturity = maturity;
}

2.1. Exercice : simplifier le premier constructeur

  1. Modifiez le code du premier constructeur pour le faire appeler le troisième

2.2. Exercice : simplifier le deuxième constructeur

  1. Maintenant, modifiez le deuxième constructeur pour le faire appeler le troisième.

3. Remplacer les constructeurs par des méthodes fabrique

L’objectif de l’opération de refactoring "Remplacer les constructeurs par des méthodes fabrique" est de remplacer les constructeurs par des méthodes fabrique qui clarifient l’intention du constructeur et renvoient des instances d’objets.

Les méthodes fabrique présentent au moins deux avantages sur les constructeurs :

  1. Elles peuvent avoir des noms différents et donc communiquer l’intention de manière efficace.

  2. Elles peuvent avoir le même nombre de paramètres.

Nous allons appliquer ce refactoring pour améliorer les constructeurs de la classe Loan. Considérons une autre version de la classe Loan, présentée dans le Listing Listing 3.

Listing 3. Tous les constructeurs de la classe Loan
public class Loan {

    double commitment;
    double outstanding;
    int riskRating;
    Date maturity;
    Date expiry;
    CapitalStrategy capitalStrategy;

    public Loan(double commitment, int riskRating, Date maturity) {
        this(commitment, 0.00, riskRating, maturity, null); // Term Loan
    }

    public Loan(double commitment, int riskRating, Date maturity, Date expiry) {
       this(commitment, 0.00, riskRating, maturity, expiry);
    }

    public Loan(double commitment, double outstanding, int riskRating, Date maturity, Date expiry) {
        this(null, commitment, outstanding, riskRating, maturity, expiry);
    }

    public Loan(CapitalStrategy capitalStrategy, double commitment, int riskRating, Date maturity, Date expiry) {
        this(capitalStrategy, commitment, 0.00, riskRating, maturity, expiry);
    }

    public Loan(CapitalStrategy capitalStrategy, double commitment, double outstanding, int riskRating, Date maturity,
            Date expiry) {
        this.commitment = commitment;
        this.outstanding = outstanding;
        this.riskRating = riskRating;
        this.maturity = maturity;
        this.expiry = expiry;
        this.capitalStrategy = capitalStrategy;

        if (capitalStrategy == null) {
            if (expiry == null)
                this.capitalStrategy = new CapitalStrategyTermLoan();
            else if (maturity == null)
                this.capitalStrategy = new CapitalStrategyRevolver();
            else
                this.capitalStrategy = new CapitalStrategyRCTL();
        }
    }
}

Pour appliquer ce refactoring, nous devons trouver un extrait de code qui appelle l’un de ces constructeurs. Par exemple, dans un cas de test :

public class CapitalCalculationTests {
    // ...
    public void testTermLoanNoPayments() {
      //...
      Loan termLoan = new Loan(commitment, riskRating, maturity);
      //...
   }
}

3.1. Exercice : extraction d’une méthode statique

Tout d’abord, appliquez le refactoring "Extraire Méthode" à un appel du constructeur pour produire une méthode publique et statique appelée createTermLoan().

3.2. Exercice : déplacement de la méthode

Ensuite, appliquez le refactoring "Déplacer méthode" sur la méthode fabrique createTermLoan(), pour la déplacer vers la classe Loan.

3.3. Exercice : Inline Method

Après cette étape, nous devons trouver tous les appelants du constructeur et les mettre à jour pour appeler createTermLoan(). Puisque la méthode createTermLoan() est maintenant le seul appelant du constructeur, nous pouvons appliquer le refactoring "Inline Method" à cet appel de constructeur.

3.4. Exercice : les autres constructeurs

Répétez les mêmes opérations pour les autres constructeurs de la classe Loan, afin de créer des méthodes fabrique supplémentaires.

3.5. Exercice : dernière étape

Dernière étape, comme les constructeurs ne sont utilisés que par les méthodes fabrique, ils peuvent devenir privés.

Références

  • [Kerievsky:2004] Kerievsky, Joshua. Refactoring to patterns. Pearson Deutschland GmbH, 2005.