Plus j'en apprends sur les différents paradigmes de programmation, tels que la programmation fonctionnelle, plus je commence à remettre en question la sagesse des concepts de POO comme l'héritage et le polymorphisme. J'ai appris pour la première fois l'héritage et le polymorphisme à l'école, et à l'époque le polymorphisme semblait être une merveilleuse façon d'écrire du code générique qui permettait une extensibilité facile.
Mais face au typage du canard (à la fois dynamique et statique) et aux caractéristiques fonctionnelles telles que les fonctions d'ordre supérieur, j'ai commencé à considérer l'héritage et le polymorphisme comme imposant une restriction inutile basée sur un ensemble fragile de relations entre les objets. L'idée générale derrière le polymorphisme est que vous écrivez une fonction une fois, et que vous pouvez ensuite ajouter de nouvelles fonctionnalités à votre programme sans changer la fonction d'origine - tout ce que vous devez faire est de créer une autre classe dérivée qui implémente les méthodes nécessaires.
Mais c'est beaucoup plus simple à réaliser grâce à la frappe de canard, que ce soit dans un langage dynamique comme Python ou un langage statique comme C ++.
À titre d'exemple, considérons la fonction Python suivante, suivie de son équivalent C ++ statique:
def foo(obj):
obj.doSomething()
template <class Obj>
void foo(Obj& obj)
{
obj.doSomething();
}
L'équivalent OOP serait quelque chose comme le code Java suivant:
public void foo(DoSomethingable obj)
{
obj.doSomething();
}
La différence majeure, bien sûr, est que la version Java nécessite la création d'une interface ou d'une hiérarchie d'héritage avant de fonctionner. La version Java implique donc plus de travail et est moins flexible. De plus, je trouve que la plupart des hiérarchies d'héritage du monde réel sont quelque peu instables. Nous avons tous vu les exemples artificiels de formes et d'animaux, mais dans le monde réel, à mesure que les besoins de l'entreprise changent et que de nouvelles fonctionnalités sont ajoutées, il est difficile de faire un travail avant d'avoir vraiment besoin d'étirer la relation "est-un" entre sous-classes, ou bien remodeler / refactoriser votre hiérarchie pour inclure d'autres classes de base ou interfaces afin de répondre aux nouvelles exigences. Avec la saisie de canard, vous n'avez pas à vous soucier de modéliser quoi que ce soit - vous vous inquiétez simplement des fonctionnalités dont vous avez besoin.
Pourtant, l'héritage et le polymorphisme sont si populaires que je doute qu'il serait exagéré de les appeler la stratégie dominante d'extensibilité et de réutilisation du code. Alors, pourquoi l'héritage et le polymorphisme sont-ils si couronnés de succès? Suis-je en train de négliger certains avantages sérieux de l'héritage / polymorphisme par rapport au typage canard?
obj
n'y a pas dedoSomething
méthode? Une exception est-elle levée? Il ne se passe rien?