GLO-4002 - Site du cours 2023

Délégation vs abstraction

Pour cet exercice, vous pouvez démarrer un nouveau projet java standard, ou utilisez celui-ci comme base.

Afin de bien faire comprendre la différence entre Delegate et Interface, nous utiliserons l'implémentation suivante d'une classe Animal.

public class Animal {

  public void move() {
    System.out.println("je marche à quatre pattes.");
  }

  public void eat() {
    System.out.println("je mange de la viande.");
  }
}

public class Lion
    extends Animal {}

Cette classe peut sembler assez générique si les animaux supportés par notre application se limitent aux lions, guépards, hyènes tachetées, chacals et léopards.

Qu'arrive-t-il si nous devons désormais supporter le pygargue vocifère qui est un oiseau qui vole et qui ne mange que du poisson ?

Nous pouvons commencer par extraire des interfaces Eater et Mover que notre classe Animal implémentera.

public interface Mover {

  void move();
}

public interface Eater {

  void eat();
}

public class Animal
    implements Mover, Eater {

  @Override
  public void move() {
    System.out.println("je marche à quatre pattes.");
  }

  @Override
  public void eat() {
    System.out.println("je mange de la viande.");
  }
}

Maintenant, nous pourrons extraire deux délégations (MeatEater et FourLegsMover) qui seront passées en argument au constructeur de la classe Animal.

public interface Mover {

  void move();
}

public class FourLegsMover
    implements Mover {

  @Override
  public void move() {
    System.out.println("je marche à quatre pattes.");
  }
}

public interface Eater {

  void eat();
}

public class MeatEater
    implements Eater {

  @Override
  public void eat() {
    System.out.println("je mange de la viande.");
  }
}

public class Animal
    implements Mover, Eater {

  private final Mover mover;

  private final Eater eater;

  public Animal(Mover mover, Eater eater) {
    this.mover = mover;
    this.eater = eater;
  }

  @Override
  public void move() {
    mover.move();
  }

  @Override
  public void eat() {
    eater.eat();
  }
}

Désormais, nous avons beaucoup plus de flexibilité pour supporter plusieurs aux animaux avec des habitudes alimentaires et des modes de déplacement distincts.

À titre d'exemple, voici les implémentations concrètes de la classe Lion et PygargueVocifere représentées sous forme de sous-classe de la classe Animal:

public class Lion
    extends Animal {

  public Lion() {
    super(new FourLegsMover(), new MeatEater());
  }
}

public class FlyMover
    implements Mover {

  @Override
  public void move() {

    System.out.println("je vole avec mes ailes.");
  }
}

public class FishEater
    implements Eater {

  @Override
  public void eat() {
    System.out.println("je mange du poisson.");
  }
}

public class PygargueVocifere
    extends Animal {

  public PygargueVocifere() {
    super(new FlyMover(), new FishEater());
  }
}

Finalement, voici une représentation UML du design que nous venons de créer.

Diagramme UML

Solution

Vous pouvez trouvez une solution potentielle dans la branche solution du dépôt github fourni ci-haut.