Salut !
J'ai vu qu'en C# on pouvait écrire des extensions pour des classes qui ne seraient pas héritables/dérivables, ce qui m'a fait réfléchir à la question : mais pourquoi est-ce que je décidrais que ma classe personne ne devra en hériter ?
Salut !
J'ai vu qu'en C# on pouvait écrire des extensions pour des classes qui ne seraient pas héritables/dérivables, ce qui m'a fait réfléchir à la question : mais pourquoi est-ce que je décidrais que ma classe personne ne devra en hériter ?
Hello,
Le danger avec l'héritage (et surtout la surcharge) c'est d'altérer une propriété implicite de la classe mère et d'aboutir à des bugs.
Un exemple bien connu d'héritage défectueux est de faire dériver une classe Carré d'une classe Rectangle. Si le setter de Carre.Hauteur est redéfini dans la classe Carré pour systématiquement fixer la Largeur à la même valeur (puisqu'un carré doit toujours avoir les côtés de même longueur), on a une propriété du Rectangle qui est violée, à savoir :
Un consommateur de Rectangle s'attend à ce que
Cette propriété n'est plus respectée si on passe à ce consommateur de Rectangle un Carré tel que défini précédemment puisque la surface vaudra dans les faits x*x et non plus x*y.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 r.Largeur = y; r.Hauteur = x; r.Surface // doit toujours retourner x*y
Bien sûr c'est un exemple basique mais il est d'autant plus valable pour des bases de code plus complexes. Donc pour éviter la tentation qu'aurait un autre développeur d'hériter de notre classe et de surcharger son comportement, en craignant qu'il puisse mal la surcharger, on scelle la classe. On voit ça parfois dans des bibliothèques ou frameworks.
Une classe scellée peut aussi être plus performante. Cf https://docs.microsoft.com/en-us/dot...elines/sealing
J'ai toujours été totalement contre cette mode du private et du sealing de classes en POO, car pour moi cela va à l'encontre d'un des 3 piliers de la POO : la réutilisabilité.
Et on en manque tellement en développement de réutilisabilité. Mais ca redevient à la mode avec des frameworks de type composants.
En quoi sceller une classe empêche de la réutiliser ?
Tu ne peux pas modifier son comportement, ce qui limite quand même fortement sa réutilisation. La capacité à hériter est une grande partie de la réutilisation des classes.
Le concepteur d'une classe ne sait pas tout ce que d'autres voudront faire avec sa classe, d'où l'intérêt pour moi de laisser ouvert au maximum en POO.
Rien de plus frustrant d'être bloqué, comme il y a quelques années en DotNet, sur une classe de Microsoft complètement Sealed et en plus de ne pas en avoir le code source.
Comme je le disais, c'est compréhensible de vouloir empêcher qu'on hérite de sa classe si on craint que les gens ne surchargent en violant des préconditions et invariants.
Et il vaut souvent mieux utiliser la composition que l'héritage pour réutiliser de l'existant. L'idée, c'est que plutôt que de bidouiller un truc qui existe via l'héritage, on va s'en servir tel quel et adapter le résultat qu'il nous retourne.
C'est une manière de faire, qui peut fonctionner dans certains cas, mais clairement pas une solution générale. En composition par interface, tu dois non-seulement avoir les interfaces qui correspondent à ce que tu veux changer (ce qui est aussi très rarement le cas, d'autant plus quand tu n'as pas forcément accès au code source), mais en plus, tu dois très souvent réécrire des méthodes que tu ne voulais pas réécrire : et c'est bien là tout l'intérêt de l'héritage de ne pas avoir à le faire.
Du moment qu'on a accès au code source, les Private et autres Sealed (C# ici), de mon point de vue, devrait être bannis. Si le code est bien documenté (et donc le contrat de fonctionnement et d'utilisation clair), aucune raison de bloquer quelque chose sous prétexte que "peut-être" ce sera mal utilisé. Mais c'est juste mon avis.
Ben, franchement, sur des algos sensibles(pas dans le sens de difficile, dans le sens de sensible métier), genre le calcul des arrondis en comptabilité bancaire, il me semble au contraire nécessaire de rendre tout ça le plus inaccessible possible au commun du programmeur bancaire. Mais c'est une décision fonctionnelle, pas technique.
En fait on est en train de parler dans le vide, ça dépend entièrement du but recherché :
- Etendre un comportement d'un framework à un point d'accroche prévu par ses concepteurs : ils doivent fournir une classe mère non sealed ou plus commodément une classe abstraite ou une interface à implémenter.
- Etendre un comportement d'un framework à un endroit pas prévu par ses concepteurs : attention à la faisabilité. Il vaut mieux ne pas ouvrir la boîte et passer au point suivant.
- Utiliser une classe ou une partie d'une bibliothèque fournie : son fournisseur est quand même le mieux placé pour savoir comment bien l'utiliser et c'est tout à fait légitime qu'il guide les consommateurs de manière contraignante. On compose (relation de type "avoir") cette classe pour l'utiliser telle quelle. Si on veut rendre le comportement polymorphique et que la classe n'a pas d'interface, on peut la wrapper dans une classe à nous qui a une interface (ça permet aussi de n'exposer que les opérations de la classe tierce dont tu as réellement besoin).
- Détourer un petit bout de logique d'une classe tierce pour le greffer dans notre code : il faut évidemment le code source parce que là on veut "ouvrir la boîte" fournie et piquer une "pièce du moteur". Dans ce cas-là, sealed ne gêne pas.
- Amender un comportement buggé d'une bibliothèque tierce : le bug report est encore la meilleure solution. Si on a le code source et du temps, on peut fixer le bug et à ce moment-là autant envoyer une pull request dessus. Hériter et surcharger en se substituant aux auteurs de la bibliothèque peut marcher sur des choses très simples, mais ça devient très risqué sur du complexe : c'est consommateur de temps pour vérifier les effets de bord, sujet à fragilité lors de la mise à jour de version, etc.
Très beau résumé Luckyluke Je suis tout à fait d'accord avec tout, sauf avec ce point.
Un notion de contrat ne devrait pas être contraignant au niveau de son code : du moment que le contrat d'utilisation est clair (documentation, code source disponible, interfaces et/ou abstractions déjà présentes), un développeur saura l'utiliser de la manière envisagée par son créateur initial, il a toute les cartes en main pour ça.
Mais de là à forcer cette limitation par le développeur initial, alors qu'il est strictement impossible de connaître tous les cas d'utilisation futurs, j'ai toujours trouvé ça très dommage. Charge à celui qui veut utiliser les briques d'une autre manière, sachant pertinemment comment il devrait normalement les utiliser, de supporter les conséquences de ses besoins.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager