GLO-4002 - Site du cours 2023

Temps estimé: 20 minutes

Exercices à propos du LSP

Question 1

Définissez dans vos mots ce qu'est le Liskov Substitution Principle.

Les classes enfants pourraient remplacer la classe parent sans que son utilisation ne soit brisée. Elle doit respecter le contrat implicite et explicite qui lie la classe mère avec ses clients. Exemple, les règles de ce contrat ne peuvent pas être plus restrictives que dans la classe mère.

Question 2

De quelles manières une classe enfant peut briser le contrat qui la lie à son parent et ainsi ne pas respecter le LSP?

À noter qu'avec un langage typé comme Java, la plupart de ces points sont déjà vérifiés à la compilation.

Entre autres:

  • Si le contrat implcite du domaine d'affaires n'est pas respecté (le cas le plus courant et le plus dangereux).
  • Si elle retourne un objet qui n'est pas compatible avec l'objet que la méthode parent retourne.
  • Si elle lance une exception que sa classe parent ne retourne pas.
  • Si elle change la signature de la méthode parent.
  • Si elle introduit des effets secondaires pour le client que la méthode parent ne provoque pas.

Question 3

Quelle est la différence entre une relation type/subtype vs class/subclass ? (Lire sur syntaxe vs sémantique)

Une relation type/subtype fait référence à une relation de polymorphisme. Exemple: la classe Dog qui est la classe mère et Poodle la classe enfant où Poodle est un type de Dog. La relation est dite sémantique, c'est-à-dire par rapport au comportement, ici il y a héritage du comportement.

Une relation class/subclass quant à elle fait référence à une relation de syntaxe où il y a réutilisation du code (forme, signature de la méthode, paramètre de retour).

Question 4

Dites si le LSP est respecté dans ce cas:


public class Vehicle {

   public void startVehicle() {
    ...
   }

}

public class Car extends Vehicle {

    @Override
    public void startVehicle() {
        super.startVehicle();
   }

   ...
}

public class Truck extends Vehicle {

    @Override
    public void startVehicle() {
        this.startMileageTracker();
        super.startVehicle();
    }

}

public class GasStation {

    public void refillVehicle(Vehicle vehicle) {
        ...
    }

}

public class PremiumGasStation extends GasStation {

    @Override
    public void refillVehicle(Vehicle vehicle) {
        if(vehicle.isType(Car)) {
            throw new RefillCarException();
        }
        super.refillVehicle();
    }

    ...
}

Ici il y a non respect du LSP pour car la classe enfant PremiumGasStation tient compte du type de véhicule pour appliquer une condition particulière que la méthode de sa classe mère n'a pas. Ce qui veut dire que si on remplaçait semaine7/ocp-exercises par PremiumGasStation, les clients qui utilisent GasStation seraient impactés.

À noter que l'ajout de startMileageTracker dans la méthode startVehicle du camion est légitime si on sait que cela ne brise pas le comportement principal et que c'est une règle attendue par les clients pour les camions.

Question 5

Comment peut-on ne pas respecter le LSP lorsque nous avons une interface avec la même signature?

Lorsque le contrat entre l'interface et ses implémentations n'est pas respecté même si les paramètres d'entrée/sortie sont les mêmes. En effet, si le comportement attendu n'est pas le même que celui délivré, le contrat n'est pas respecté. Par exemple, une interface ayant la méthode copy s'il est entendu que les enfants doivent implémenter une deep copy, mais qu'ils implémentent plutôt une shallow copy, alors le contrat n'est pas respecté.

Question 6

En quoi peut-il y avoir un lien entre le LSP et le ISP?

ISP fait référence à un client (la classe qui utilise/appelle l'interface) qui ne doit pas dépendre de méthodes qu'il n'utilise pas. Le LSP quant à lui stipule que la classe enfant doit respecter le contrat défini avec sa classe mère. Donc, si la classe enfant ignore le comportement de sa classe mère, l'utilisateur dépend alors indirectement de comportements ignorés et pourrait avoir une surprise.

Question 7

En quoi peut-il y avoir un lien entre le LSP et l'OCP?

Lorsqu'il y a non respect du LSP, il peut y avoir une indication que l'OCP n'est/ne sera pas respecté. En effet, car s'il y a ajout d'une fonctionnalité, il est probable qu'il faille modifier le code existant pour y répondre. Exemple, le non respect du LSP qui amène à devoir faire des vérifications selon le type (instanceof), il faut donc ajouter une vérification pour répondre à un nouveau besoin.

Question 8

Nommez une raison pour laquelle il est important de respecter le LSP.

Entre autres, le non respect du LSP peut être un signe que l'abstraction a été établie prématurément et est incorrecte. S'il faut valider le code le type de la classe pour appliquer certaines règles, on perd le concept/ l'utilité du polymorphisme et il peut y avoir une duplication du code conditionnel partout. Cela peut être très long de trouver l'erreur car souvent une violation du LSP fait briser quelque chose de visible dans la chaîne d'appel et non là où est la violation.