L'héritage et l'OO, faire différemment ça peut être facile tellement les concepts sont mal maitrisés.
Typiquement, j'ai du monter une formation OO pour le taf' il y a 2 mois et j'ai cherché ce que l'on pouvait trouver comme exercices et exemples. Et que voyais-je ? Un accent systématique sur l'organisation des données. Des conceptions de bases de données en veux-tu, en voilà : gestion de bibliothèque, gestion de stock de pharmacie.
Alors que l'objet, si on reprend les définitions des pionniers, c'est avant tout du comportement et des messages qui circulent entre objets, et du extreme late binding of all things. Bref. Le meilleur exo que j'ai trouvé, ce fut celui du Javaquarium (et encore, on peut y observer les limites du paradigmes OO, avec les ECS qui se prêtent bien à l'exercice.)
Autre exemple de la totale non maitrise du concept objet : la quantité d'agrégats de données (dépourvus d'invariants) organisés à coup de getters et de setters parce que "il est inadmissible de donner un accès direct aux données". Ben oui il faut encapsuler. Sauf qu'il y a confusion. Si l'encapsulation se fait techniquement par masquage (cachage, ça sonne pas bien!) des données dans beaucoup de langages (hormis Python et Eiffel p.ex.), son but n'est pas de cacher, mais de protéger les invariants. Mais non. Il faut absolument cacher toutes les données d'un agrégat, même s'il n'a aucun invariant.
Dans les trucs qui ne vont pas, vous trouverez aussi plein de présentations techniques de l'héritage où l'on voit des PointColore dériver de Point. Or, dans les classes valeurs, l'héritage ça ne marche pas. Cf une démonstration que l'on trouve dans Effective Java de J.Bloch -> on ne peut pas avoir une méthode equals qui respecte à la fois la transitivité, la symétrie et le LSP.
Pour l'héritage, dans nos langages, la technique de l'héritage répond à deux besoins: l'import de code, et la substituabilité/le sous-typage. Encore une autre piste à creuser si on veut se distinguer.
Ce qui me fait penser à la critique que je fais aux cours historiques de C++ : ils présentent la syntaxe du langage sans s'intéresser aux besoins. Pour chaque besoin, il va y avoir un "pattern", ou "idiome" qui correspond. Et ces derniers vont se retranscrire en éléments syntaxiques associés. Si l'approche syntaxique était tolérable en BASIC à partir du moment où on suppose que le développeur connait variables et les principes des structures de contrôle. Ça ne marche plus très bien avec certains langages modernes comme le C++.
EDIT: je ne parle là que de contenu. Il y a après la façon de présenter les choses. Et là, comme toujours, il est important de connaitre son public. Pour des tutos en lignes, c'est plus complexe de cibler un public bien précis. Quand je le peux, j'essaie de me raccrocher à ce qu'il connaissent en termes de besoin propres à leur métier. Ainsi quand je présente les concepts objets, je cherche à les raccrocher aux équivalents qu'ils connaissent et de les démystifier en expliquant à quel besoin va répondre tel ou tel concept. Et comment la nouvelle réponse se distingue des alternatives qu'ils pouvaient connaitre. Je tâche à chaque fois de disposer de métaphores dans le métier du public ou dans la vie de tous les jours.