Les principes SOLID appliqués en C# (3/5)
Publié le 23/08/2022
Par  Christophe MOMMER

Avant toute chose, il faut savoir que chacun de ces principes peut également être découvert en vidéo. La vidéo pour le second principe se trouve ici : https://www.youtube.com/watch?v=Jj3ilspEj7o

L pour Liskov Substitution Principle

Ce principe porte un nom étrange, et c'est probablement aussi celui qui donne le plus de problèmes pour être expliqué (et compris ...). Je vais essayer de faire simple dans ce billet !

C'est quoi en fait ?

Derrière ce nom de principe atypique se cache un principe relativement simple : toute classe plus spécifique passée à une méthode qui attends une classe plus générique ne doit pas casser le comportement. C'est un des grands pièges de l'objet. L'héritage est une fonctionnalité ultra puissante, mais mal maîtrisée, elle peut avoir des effets de bords désastreux.

L'exemple le plus simple pour illustrer ce genre de souci est de casser avec une exception. Prenons le code suivant comme base de travail :

public abstract class Animal
{
   public void Marcher();
}

public class Chien : Animal
{
   public override void Marcher() {  Console.WriteLine("Le chien marche"); }
}

public class Promeneur
{
    public void Promener(Animal a)
    {
         a.Marcher();
    }
}

Ici, la classe Promeneur fait marcher un animal. On peut lui spécifier n'importe quelle classe qui hérite de la classe Animal. Donc, si on appelle la méthode Promener avec une instance de Chien, c'est valide et ça va fonctionner.

Ok, et il est où le problème en fait ?

Ici, on a définit un comportement commun dans la classe Animal qui est marché. Mais que se passe-t-il si ... 

public class Poisson : Animal
{
   public override void Marcher() { throw new InvalidOperationException("Un poisson, ça nage, ça ne marche pas ... !"); }
}

Au niveau objet, cette approche est valide (mais désastreuse). Au niveau "logique" aussi : un poisson est un animal après tout ! Sauf que si je demande à mon promeneur de promener le poisson (j'avoue, ça devient du n'importe quoi à ce niveau là 😅), et bien ... BOOM, exception ! On n'a pas respecté le principe, car une classe plus spécifique casse le fonctionnement de la classe de base.

Bon, ici c'est violent et flagrant car on a une exception. Mais le comportement peut être plus vicieux. Par exemple, imaginez une classe qui traite des règles de calculs diverses et variées, et chaque règle est implémentée dans une classe héritant de la classe mère Regle. Puis, une classe de calcul spécifique, dans un cas bien précis renvoie 0, qui est une valeur invalide car le moteur divise généralement par le résultat de ce calcul. DivideByZeroException ...

Ça peut aller loin ! C'est juste ça Liskov Substitution Principle : assurez-vous que vos classes filles ne cassent pas le programme quand vous les passer à une méthode qui attendent une classe spécifique.

D'ailleurs, pour l'anecdote, c'est pour ça que certains développeurs préfèrent éviter l'héritage. Je n'irai pas jusque là, mais soyez vigilants ...