ConstructiCat Logo
CodeBust.
Browse section ▾

Remplacer le conditionnel par du polymorphisme.

Problem

Vous avez une instruction conditionnelle qui exécute diverses actions selon le type ou les propriétés d’un objet.

Solution

Créez des sous-classes correspondant aux branches de la conditionnelle. Dans chacune, créez une méthode commune et déplacez-y le code de la branche correspondante de la conditionnelle. Remplacez ensuite la conditionnelle par l’appel de la méthode appropriée. Le résultat est que la bonne implémentation sera obtenue par polymorphisme en fonction de la classe de l’objet.

##Example

Before
class Bird {
  // ...
  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");
  }
}
After
abstract class Bird {
  // ...
  abstract double getSpeed();
}

class European extends Bird {
  double getSpeed() {
    return getBaseSpeed();
  }
}
class African extends Bird {
  double getSpeed() {
    return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
  }
}
class NorwegianBlue extends Bird {
  double getSpeed() {
    return (isNailed) ? 0 : getBaseSpeed(voltage);
  }
}

// Quelque part dans le code client
speed = bird.getSpeed();

##Why Refactor

Cette technique de refactoring peut vous aider si votre code contient des opérateurs effectuant diverses tâches qui varient en fonction :

  • de la classe de l’objet ou de l’interface qu’il implémente

  • de la valeur d’un champ de l’objet

  • du résultat de l’appel de l’une des méthodes de l’objet

Si une nouvelle propriété ou un nouveau type d’objet apparaît, vous devrez rechercher et ajouter du code dans toutes les conditionnelles similaires. Le bénéfice de cette technique est donc multiplié s’il existe plusieurs conditionnelles disséminées dans toutes les méthodes d’un objet.

##Benefits

  • Cette technique respecte le principe Tell-Don’t-Ask : au lieu d’interroger un objet sur son état puis d’exécuter des actions en conséquence, il est bien plus simple de dire à l’objet ce qu’il doit faire et de le laisser décider lui-même comment le faire.

  • Supprime le code dupliqué. Vous vous débarrassez de nombreuses conditionnelles presque identiques.

  • Si vous devez ajouter une nouvelle variante d’exécution, il vous suffit d’ajouter une nouvelle sous-classe sans toucher au code existant (principe ouvert/fermé).

##How to Refactor

Préparation au refactoring

Pour cette technique de refactoring, vous devez disposer d’une hiérarchie de classes déjà prête qui contiendra les comportements alternatifs. Si vous n’avez pas de hiérarchie de ce genre, créez-en une. D’autres techniques vous aideront à y parvenir :

  • Remplacer le code de type par des sous-classes. Des sous-classes seront créées pour toutes les valeurs d’une propriété particulière de l’objet. Cette approche est simple mais moins souple, car vous ne pouvez pas créer de sous-classes pour les autres propriétés de l’objet.

  • Remplacer le code de type par État/Stratégie. Une classe sera dédiée à une propriété particulière de l’objet et des sous-classes en seront dérivées pour chaque valeur de la propriété. La classe actuelle contiendra des références aux objets de ce type et leur déléguera l’exécution.

Les étapes suivantes supposent que vous avez déjà créé la hiérarchie.

Étapes du refactoring

  1. Si la conditionnelle se trouve dans une méthode qui exécute également d’autres actions, appliquez Extraire la méthode.

  2. Pour chaque sous-classe de la hiérarchie, redéfinissez la méthode qui contient la conditionnelle et copiez-y le code de la branche conditionnelle correspondante.

  3. Supprimez cette branche de la conditionnelle.

  4. Répétez le remplacement jusqu’à ce que la conditionnelle soit vide. Supprimez ensuite la conditionnelle et déclarez la méthode abstraite.