Oh, le bel héritage
Je fais une passe très rapide (sans diff, c’est difficile de savoir ce que l’on a déjà lu ou pas ;))
comme tout bon exemple introductif, nous n’avons aucun invariant
C’est une vision assez personnelle, mais j’ai tendance à considérer qu’un objet a toujours un invariant invisible: est utilisable. Pour les appareils de nettoyage, on pourrait p.ex. avoir: "est dans un pièce qui existe".
[Débat autour du MI]
Je soupçonne que beaucoup de problèmes et de critiques autour de l’héritage multiple prennent leur source qu’un héritage qui ne respecte pas le LSP est vite un soucis, alors quand on multiple les origines, si le LSP n’est pas respecté sur tous les parents, on augmente de façon non linéaire les problèmes.
Il n’y a pas que le non respect du "ne pas hériter de classes concrète" qui pose soucis avec le MI.
Je pense aussi qu’il n’y a pas besoin de rentrer dans les détails pour l’instant. Mais un renvoi vers un chapitre ultérieur sur l’héritage virtuel, ou juste un mot comme quoi nous ne verrons pas l’héritage virtuel peut être le bien venu. Donner le terme pour ceux qui veulent creuser la piste.
+1 Pour la suite sur la conception d’armes & cie. Perso avant les ECS j’avais tendance à voir la solution comme une combinaison de traits (sortes de template method + strategy).
Le résumé final
- Il reste un "NV idiom" au lieu d’Interface.
SOLID
DIP
Un grand intérêt classique du DIP: pour l’écriture de tests unitaires. Quand le service que l’on veut tester dépend intimement d’un autre un peu plus complexe, il est intéressant d’injecter une version fake/mokée de la dépendance ce qui permet de tester notre code client sans dépendre d’aléas d’implémentation de la dépendance. Exemple pondu à la va vite: un protocole d’échange d’informations entre composants par requêtes http. On veut tester le comportement d’un des deux autres composants sans échanger par http ni en dialoguant avec un tiers composant potentiellement buggué.
Avec le DIP, on injecte donc la version moquée qui renvoie exactement ce que l’on attend pour le test unitaire.
Déméter
J’ai tendance à rapprocher cette loi avec le tell don’t ask
Autre
Dans mes formations, j’ai tendance à rajouter un disclaimer à propos de ces règles.
-
Non respect du LSP == bug. Le reste, est un indicateur de soucis en devenir de maintenance (risque de bugs, facilité à faire évoluer…)
-
DIP et ISP et LSP ne sont applicables qu’aux entités. Pas aux valeurs. Enfin, en version avec héritage ce n’est pas applicable, si on étend aux concepts du C++20, c’est de nouveau applicable.
-
L’abus de tous ces principes conduit vite au FizzBuzzEnterprise, et le C++, je ne sais pourquoi, est resté un langage de pragmatiques.
Aller plus loin
Là, @lmghs démonte cinq mythes courants sur le C++
Euh… non, c’est Loic qui traduit un article de Bjarne Stroustrup.
Un autre livre un peu ancien mais dont les principes restent valables s’appelle Design Patterns - Tête la première, par Eric Freeman, Elisabeth Freeman, Kathy Sierra et Bert Bates, connus sous le nom de Gang of four
Le Gang of Four, c’est (Eric?) Gamma, (John?) Vlisside et deux autres que j’ai honteusement oublié. Leur livre qui est le point de départ du terme design pattern, est Design Pattern, Elements of reusable code(/technology?) (je crois), aka GoF par référence aux 4 auteurs, sorti en 95 avez Addisson Wesley (AW est l’éditeur de référence en matière d’ouvrages consacrés aux C++98/03, mais pas que; je n’ai pas fait attention pour la suite).
Le Design Pattern head first (?) est un autre ouvrage, très pédagogique et non typé "de référence" comme le GoF peut l’être.
Après de nombreuses critiques sont applicables aux formes de certains DP dans le contexte C++
- singleton == variable globale en smoking
- pattern command => comble l’absence de support de fonctionnel/lambda
- itérateurs du C++ qui ont une autre forme
- std::visit, plus aussi autres solutions pour le double dispatch