Cet article présente principalement en détail les informations pertinentes sur le principe de substitution de Liskov, qui ont une certaine valeur de référence. Les amis intéressés peuvent se référer à
Parlons du principe de substitution de Liskov .
Définition 1 : Si pour chaque objet o1 de type T1, il existe un objet o2 de type T2, de sorte que tous les programmes P définis avec T1 sont représentés dans tous les objets o1 Lors du passage à o2, le comportement du programme P ne change pas, alors le type T2 est un sous-type du type T1.
Définition 2 : Tous les lieux qui référencent une classe de base doivent pouvoir utiliser de manière transparente les objets de ses sous-classes.
Origine du problème : Il existe une fonction P1, qui est complétée par la classe A. Il est maintenant nécessaire d’étendre la fonction P1, et la fonction développée est P, où P se compose de la fonction originale P1 et de la nouvelle fonction P2. La nouvelle fonction P est implémentée par la sous-catégorie B de la catégorie A. La sous-catégorie B peut provoquer un dysfonctionnement de la fonction d'origine P1 lors de l'exécution de la nouvelle fonction P2.
Solution : Lors de l'utilisation de l'héritage, suivez le principe de substitution de Liskov. Lorsque la classe B hérite de la classe A, sauf pour ajouter de nouvelles méthodes pour compléter la nouvelle fonction P2, essayez de ne pas remplacer les méthodes de la classe parent A et essayez de ne pas surcharger les méthodes de la classe parent A.
L'héritage contient la signification suivante : toutes les méthodes qui ont été implémentées dans la classe parent (par rapport aux méthodes abstraites) définissent en fait une série de spécifications et de contrats, bien que ce ne soit pas le cas. Il est obligatoire pour tous les sous-classes doivent se conformer à ces contrats, mais si les sous-classes modifient arbitrairement ces méthodes non abstraites, cela endommagera l'ensemble du système d'héritage. Le principe de substitution de Liskov exprime ce sens.
En tant que l'une des trois caractéristiques majeures de l'orientation objet, l'héritage apporte une grande commodité à la programmation, mais il présente également des inconvénients. Par exemple, l'utilisation de l'héritage entraînera une intrusion dans le programme, réduira la portabilité du programme et augmentera le couplage entre les objets. Si une classe est héritée par d'autres classes, lorsque cette classe doit être modifiée, toutes les sous-classes doivent être prises en compte. . et une fois la classe parent modifiée, toutes les fonctions impliquant la sous-classe peuvent mal fonctionner.
Pour donner un exemple du risque d'héritage, nous devons compléter une fonction de soustraction de deux nombres, qui est gérée par la classe A.
class A{ public int func1(int a, int b){ return a-b; } } public class Client{ public static void main(String[] args){ A a = new A(); System.out.println("100-50="+a.func1(100, 50)); System.out.println("100-80="+a.func1(100, 80)); } }
Résultats en cours d'exécution :
100-50=50
100-80=20
Plus tard, nous devrons ajouter une nouvelle fonction : compléter l'addition de deux nombres, puis la sommer avec 100, ce qui relève de la responsabilité de la classe B. Autrement dit, la classe B doit remplir deux fonctions :
Soustraire deux nombres.
Ajoutez les deux nombres, puis ajoutez 100.
Puisque la classe A a déjà implémenté la première fonction, une fois que la classe B a hérité de la classe A, il lui suffit de compléter la deuxième fonction. Le code est le suivant :
.
class B extends A{ public int func1(int a, int b){ return a+b; } public int func2(int a, int b){ return func1(a,b)+100; } } public class Client{ public static void main(String[] args){ B b = new B(); System.out.println("100-50="+b.func1(100, 50)); System.out.println("100-80="+b.func1(100, 80)); System.out.println("100+20+100="+b.func2(100, 20)); } }
Une fois la classe B terminée, le résultat en cours d'exécution est :
100-50=150
100 - 80=180
100+20+100=220
Nous avons constaté que la fonction de soustraction qui s'exécutait à l'origine présentait normalement une erreur. La raison en est que la classe B réécrit par inadvertance la méthode de la classe parent lors du nom de la méthode, ce qui oblige tous les codes qui exécutent la fonction de soustraction à appeler la méthode réécrite de la classe B, provoquant des erreurs dans les fonctions qui s'exécutaient normalement à l'origine. Dans cet exemple, après avoir référencé la fonction complétée par la classe de base A et l'avoir remplacée par la sous-classe B, une exception s'est produite. Dans la programmation réelle, nous complétons souvent de nouvelles fonctions en réécrivant la méthode de la classe parent. Bien que ce soit simple à écrire, la réutilisabilité de l'ensemble du système d'héritage sera relativement faible, en particulier lorsque le polymorphisme est fréquemment utilisé, le risque d'erreurs d'exécution du programme est élevé. très élevé. Si vous devez réécrire la méthode de la classe parent, une approche plus courante est la suivante : la classe parent et la sous-classe d'origine héritent toutes deux d'une classe de base plus populaire, suppriment la relation d'héritage d'origine et utilisent à la place la dépendance, l'agrégation, la combinaison et d'autres relations.
En termes simples, le principe de substitution de Liskov est le suivant : Les sous-classes peuvent étendre les fonctions de la classe parent, mais elles ne peuvent pas modifier les fonctions d'origine de la classe parent. Il contient les 4 niveaux de signification suivants :
Une sous-classe peut implémenter la méthode abstraite de la classe parent, mais ne peut pas remplacer la méthode non abstraite du parent classe.
Les sous-classes peuvent ajouter leurs propres méthodes uniques.
Lorsqu'une méthode d'une sous-classe remplace une méthode d'une classe parent, les conditions préalables de la méthode (c'est-à-dire les paramètres formels de la méthode) sont plus souples que les paramètres d'entrée de la méthode de classe parent.
Lorsqu'une méthode d'une sous-classe implémente une méthode abstraite d'une classe parent, les postconditions de la méthode (c'est-à-dire la valeur de retour de la méthode) sont plus strictes que celles de la classe parent .
Cela semble incroyable, car nous constaterons que nous violons souvent le principe de substitution de Liskov dans notre propre programmation, mais le programme fonctionne toujours bien. Donc tout le monde se posera cette question : quelles seront les conséquences si j’insiste pour ne pas suivre le principe de substitution de Liskov ?
La conséquence est : Le risque de problèmes avec le code que vous écrivez sera considérablement augmenté.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!