13.14. Héritage et polymorphisme en Java : principe de substitution de Liskov
La programmation orientée objet (POO) est un paradigme de programmation qui utilise des « objets » pour modéliser les données et le comportement. Java, étant un langage orienté objet, permet aux programmeurs d'exploiter des concepts tels que l'héritage et le polymorphisme pour créer des programmes plus flexibles et réutilisables. Approfondissons notre compréhension de ces concepts et explorons le principe de substitution de Liskov (LSP), l'un des principes fondamentaux de la programmation orientée objet.
Héritage en Java
L'héritage est un mécanisme par lequel une nouvelle classe, appelée sous-classe, peut hériter des champs et des méthodes d'une autre classe, appelée superclasse. Cela permet aux sous-classes de réutiliser et d'étendre le comportement des superclasses. En Java, l'héritage s'effectue avec le mot-clé extends
.
classe Véhicule {
public void move() {
System.out.println("Véhicule en mouvement");
}
}
la classe Car étend le véhicule {
@Passer outre
public void move() {
System.out.println("Voiture en mouvement");
}
}
Dans l'exemple ci-dessus, Car
est une sous-classe de Vehicle
. La sous-classe Car
hérite de la méthode move()
et fournit sa propre implémentation en utilisant l'annotation @Override
. Cela illustre l'extension et la réutilisation des fonctionnalités.
Polymorphisme en Java
Le polymorphisme est la capacité d'un objet à être traité comme une instance de sa propre classe ou de toute classe dont il hérite. En Java, cela se fait généralement via le remplacement et la surcharge de méthodes.
Avec la substitution de méthode, une sous-classe peut fournir une implémentation spécifique d'une méthode qui existe déjà dans sa superclasse. La surcharge de méthodes permet à plusieurs méthodes d'avoir le même nom, mais avec des listes de paramètres différentes.
Principe de substitution de Liskov (LSP)
Le principe de substitution de Liskov, formulé par Barbara Liskov en 1987, est un concept fondamental de la programmation orientée objet. LSP déclare que si S
est un sous-type de T
, alors les objets de type T
dans un programme peuvent être remplacés par des objets de type S
sans modifier les propriétés souhaitables de ce programme. En d'autres termes, une sous-classe doit être remplaçable par sa superclasse sans provoquer d'erreurs.
Pour rejoindre LSP, une sous-classe doit :
- Ne modifiez pas le comportement attendu des méthodes de superclasse.
- Ne violez pas les invariants et les contrats établis par la superclasse.
- Assurez-vous que les sous-types peuvent être utilisés pour remplacer les types de base sans avoir besoin de connaître la différence entre eux.
La violation de LSP peut entraîner des bugs et un comportement inattendu dans un programme. Par exemple :
classe Oiseau {
public void fly() {
System.out.println("Oiseau qui vole");
}
}
la classe Penguin étend Bird {
@Passer outre
public void fly() {
throw new UnsupportedOperationException("Les pingouins ne volent pas");
}
}
Dans l'exemple ci-dessus, Penguin
est une sous-classe de Passaro
, mais il remplace la méthode fly()
pour lever une exception, comme les pingouins ne volent pas. Cela viole LSP car le comportement de la superclasse Bird
est modifié d'une manière qui peut provoquer des erreurs si des objets Penguin
sont utilisés dans des contextes qui s'attendent à ce que tous les oiseaux soient capables de voler.
Comment appliquer LSP en Java
Pour vous assurer que LSP est respecté en Java, suivez ces directives :
- Évitez de remplacer les méthodes d'une manière qui modifie le comportement attendu.
- Utilisez l'annotation
@Override
lors du remplacement de méthodes pour vous assurer que vous modifiez réellement une méthode de superclasse. - Si nécessaire, utilisez la composition plutôt que l'héritage pour partager des comportements entre des classes qui n'ont pas de relation de substituabilité.
- Respectez les contrats établis par la superclasse, y compris les préconditions, les postconditions et les invariants.
L'adoption de LSP permet de maintenir l'intégrité de la conception orientée objet, favorisant ainsi la création de systèmes plus robustes et plus maintenables.
Conclusion
L'héritage et le polymorphisme sont des concepts puissants en Java qui, lorsqu'ils sont utilisés correctement, peuvent augmenter la réutilisation et la flexibilité du code. Le principe de substitution de Liskov est essentiel pour utiliser efficacement ces concepts, garantissant que les sous-classes peuvent être utilisées à la place de leurs superclasses sans provoquer d'effets secondaires indésirables. Lors de la conception et de l'implémentation de vos classes Java, gardez toujours LSP à l'esprit pour créer un code plus cohérent.fiable et facile à entretenir.