La programmation orientée objet est morte. Attends, vraiment ?

La programmation dans les années 1960 avait un gros problème : les ordinateurs n’étaient pas encore si puissants, et d’une manière ou d’une autre ils devaient diviser les capacités entre les structures de données et les procédures.
Cela signifiait que si vous disposiez d’un grand nombre de données, vous ne pouviez pas en faire autant sans pousser un ordinateur à ses limites. D’un autre côté, si vous deviez faire beaucoup de choses, vous ne pouviez pas utiliser trop de données ou l’ordinateur prendrait une éternité.
Puis Alan Kay est venuré en 1966 ou 1967 et a théorisé que l’on pouvait utiliser des mini-ordinateurs encapsulés qui ne partageaient pas leurs données, mais communiquaient plutôt par messagerie. De cette façon, les ressources de calcul pourraient être utilisées de manière beaucoup plus économique.
Malgré l’ingéniosité de l’idée, il faudrait jusqu’en 1981 jusqu’à ce que la programmation orientée objet se généralise. Depuis lors, cependant, il n’a cessé d’attirer des développeurs de logiciels nouveaux et chevronnés. Le marché pour les programmeurs orientés objet est plus occupé que jamais.
Mais ces dernières années, le paradigme vieux de dix ans a reçu de plus en plus de critique. Se pourrait-il que, quatre décennies après que la programmation orientée objet ait atteint les masses, la technologie dépasse ce paradigme ?
Contents
Le couplage de fonctions avec des données est-il si stupide ?
L’idée principale derrière la programmation orientée objet est aussi simple que possible : vous essayez de diviser un programme en parties qui sont aussi puissantes que l’ensemble. Il s’ensuit que vous couplez des données et des fonctions qui ne sont utilisées que sur les données en question.
Notez que cela ne couvre que la notion d’encapsulation, c’est-à-dire que les données et les fonctions qui se trouvent à l’intérieur d’un objet sont invisibles à l’extérieur. On ne peut interagir avec le contenu d’un objet que par le biais de messages, généralement appelés fonctions getter et setter.
Ce qui n’est pas contenu dans l’idée initiale, mais qui est considéré comme essentiel à la programmation orientée objet aujourd’hui, c’est l’héritage et le polymorphisme. L’héritage signifie essentiellement que les développeurs peuvent définir des sous-classes qui ont toutes les propriétés de leur classe parente. Cela n’a été introduit dans la programmation orientée objet qu’en 1976, une décennie après sa conception.
Le polymorphisme est venu à la programmation orientée objet une autre décennie plus tard. En termes simples, cela signifie qu’une méthode ou un objet peut servir de modèle pour d’autres. Dans un sens, c’est une généralisation de l’héritage, car toutes les propriétés de la méthode ou de l’objet d’origine n’ont pas besoin d’être transmises à la nouvelle entité ; à la place, vous pouvez choisir de remplacer les propriétés.
La particularité du polymorphisme est que même si deux entités dépendent l’une de l’autre dans le code source, une entité appelée fonctionne plus comme un plugin. Cela facilite la vie des développeurs car ils n’ont pas à se soucier des dépendances lors de l’exécution.
Il convient de mentionner que l’héritage et le polymorphisme ne sont pas exclusifs à la programmation orientée objet. Le véritable différenciateur est l’encapsulation des données et des méthodes qui leur appartiennent. À une époque où les ressources informatiques étaient beaucoup plus rares qu’aujourd’hui, c’était une idée géniale.
Les cinq grands problèmes de la programmation orientée objet
Une fois que la programmation orientée objet a touché les masses, elle a transformé la façon dont les développeurs voient le code. Ce qui prévalait avant les années 1980, la programmation procédurale, était très orienté machine. Les développeurs devaient en savoir un peu plus sur le fonctionnement des ordinateurs pour écrire du bon code.
En encapsulant les données et les méthodes, la programmation orientée objet a rendu le développement logiciel plus centré sur l’humain. Cela correspond à l’intuition humaine que la méthode drive()
appartient au groupe de données car
, mais pas au groupe teddybear
.
Lorsque l’héritage est arrivé, c’était aussi intuitif. Il est parfaitement logique que Hyundai
est un sous-groupe de car
et partage les mêmes propriétés, mais PooTheBear
ne fait pas.
Cela ressemble à une machinerie puissante. Le problème, cependant, est que les programmeurs qui ne connaissent que le code orienté objet forceront cette façon de penser sur tout ce qu’ils font. C’est comme quand les gens voient des clous partout parce qu’ils n’ont qu’un marteau. Comme nous le verrons ci-dessous, lorsque votre boîte à outils ne contient qu’un marteau, cela peut entraîner des problèmes fatals.
Le problème de la jungle des gorilles de banane
Imaginez que vous mettez en place un nouveau programme et que vous songez à concevoir une nouvelle classe. Ensuite, vous repensez à une jolie petite classe que vous avez créée pour un autre projet, et vous réalisez qu’elle serait parfaite pour ce que vous essayez de faire actuellement.
Aucun problème! Vous pouvez réutiliser la classe de l’ancien projet pour votre nouveau.
À l’exception du fait que cette classe peut en fait être une sous-classe d’une autre classe, vous devez donc maintenant inclure la classe parente. Ensuite, vous vous rendez compte que la classe parent dépend également d’autres classes, et vous finissez par inclure des tas de code.
Le créateur d’Erlang, Joe Armstrong, célèbre proclamé:
Le problème avec les langages orientés objet est qu’ils ont tout cet environnement implicite qu’ils transportent avec eux. Vous vouliez une banane, mais vous avez obtenu un gorille tenant la banane et toute la jungle.
Cela dit à peu près tout. C’est bien de réutiliser les classes ; en fait, cela peut être une vertu majeure de la programmation orientée objet.
Mais ne le poussez pas à l’extrême. Parfois, vous feriez mieux d’écrire une nouvelle classe au lieu d’inclure des masses de dépendances pour le plaisir de DRY (ne vous répétez pas).
Le problème de la classe de base fragile
Imaginez que vous avez réutilisé avec succès une classe d’un autre projet pour votre nouveau code. Que se passe-t-il si la classe de base change ?
Cela peut corrompre tout votre code. Vous n’y avez peut-être même pas touché. Mais un jour, votre projet fonctionne à merveille, le lendemain, ce n’est pas le cas parce que quelqu’un a modifié un détail mineur dans la classe de base qui s’avère crucial pour votre projet.
Plus vous utilisez l’héritage, plus vous avez potentiellement à faire de maintenance. Ainsi, même si la réutilisation du code semble très efficace à court terme, elle peut devenir coûteuse à long terme.
Le problème du diamant
L’héritage est cette petite chose mignonne où nous pouvons prendre les propriétés d’une classe et les transférer à d’autres. Mais que faire si vous voulez mélanger les propriétés de deux classes différentes ?
Eh bien, vous ne pouvez pas le faire. Du moins pas d’une manière élégante. Considérons par exemple la classe Copier
. (J’ai emprunté cet exemple, ainsi que quelques informations sur les problèmes présentés ici, de Charles Scalfani histoire virale Au revoir, la programmation orientée objet.) Un copieur numérise le contenu d’un document et l’imprime sur une feuille vide. Donc, devrait-il être la sous-classe de Scanner
, ou de Printer
?
Il n’y a tout simplement pas de bonne réponse. Et même si ce problème ne va pas casser votre code, il revient assez souvent pour être frustrant.
Le problème de la hiérarchie
Dans le problème du diamant, la question était de savoir quelle classe Copier
est une sous-classe de. Mais je vous ai menti – il y a une bonne solution. Laisser Copier
être la classe parente, et Scanner
et Printer
être des sous-classes qui héritent uniquement d’un sous-ensemble des propriétés. Problème résolu !
C’est chouette. Mais que faire si votre Copier
n’est qu’en noir et blanc, et votre Printer
peut gérer la couleur aussi? N’est-ce pas Printer
en ce sens une généralisation de Copier
? Et qu’est-ce qui se passerait si Printer
est connecté au WiFi, mais Copier
n’est pas?
Plus vous accumulez de propriétés sur une classe, plus il devient difficile d’établir des hiérarchies appropriées. En réalité, vous avez affaire à des groupes de propriétés, où Copier
partage certaines, mais pas toutes les propriétés de Printer
, et vice versa. Et si vous essayez de mettre cela dans des hiérarchies et que vous avez un gros projet complexe, cela pourrait vous conduire à un désastre désordonné.
Le problème de référence
Vous pourriez dire, d’accord, alors nous ferons simplement de la programmation orientée objet sans hiérarchies. Au lieu de cela, nous pourrions utiliser des clusters de propriétés et hériter, étendre ou remplacer les propriétés selon les besoins. Bien sûr, ce serait un peu compliqué, mais ce serait une représentation précise du problème à résoudre.
Il y a juste un problème. L’objectif de l’encapsulation est de protéger les données les unes des autres et de rendre ainsi l’informatique plus efficace. Cela ne fonctionne pas sans hiérarchies strictes.
Considérez ce qui se passe si un objet A
remplace la hiérarchie en interagissant avec un autre objet B
. Peu importe la relation A
a avec B
, excepté B
n’est pas la classe parente directe. Puis A
doit contenir une référence privée à B
, car sinon, il ne pourrait pas interagir.
Mais si A
contient les informations que les enfants de B
ont également, alors ces informations peuvent être modifiées à plusieurs endroits. Par conséquent, les informations sur B
n’est plus sûr et l’encapsulation est cassée.
Bien que de nombreux programmeurs orientés objet construisent des programmes avec ce type d’architecture, il ne s’agit pas d’une programmation orientée objet. C’est juste un gâchis.
Le danger du paradigme unique
Ce que ces cinq problèmes ont en commun, c’est qu’ils implémentent l’héritage là où ce n’est pas la meilleure solution. Puisque l’héritage n’était même pas inclus dans la forme originale de la programmation orientée objet, je n’appellerais pas ces problèmes inhérents à l’orientation objet. Ce ne sont que des exemples d’un dogme poussé trop loin.
Cependant, non seulement la programmation orientée objet peut être exagérée. En pur programmation fonctionnelle, il est extrêmement difficile de traiter les entrées de l’utilisateur ou d’imprimer des messages sur un écran. La programmation orientée objet ou procédurale est bien meilleure à ces fins.
Pourtant, il y a des développeurs qui essaient d’implémenter ces choses comme des fonctions pures et de faire exploser leur code jusqu’à des dizaines de lignes que personne ne peut comprendre. En utilisant un autre paradigme, ils auraient pu facilement réduire leur code à quelques lignes lisibles.
Les paradigmes sont un peu comme les religions. Ils sont bons avec modération – sans doute, Jésus, Mohamed et Bouddha ont dit des trucs plutôt cool. Mais si vous les suivez dans les moindres détails, vous pourriez finir par rendre la vie de vous-même et des personnes qui vous entourent assez misérable.
Il en va de même pour les paradigmes de programmation. Il ne fait aucun doute que la programmation fonctionnelle est gagner en traction, alors que la programmation orientée objet a attiré certains critique sévère dans les dernières années.
Il est logique de s’informer sur les nouveaux paradigmes de programmation et de les utiliser le cas échéant. Si la programmation orientée objet est le marteau qui fait que les développeurs voient des clous partout où ils vont, est-ce une raison pour jeter le marteau par la fenêtre ? Non. Vous ajoutez un tournevis à votre boîte à outils, et peut-être un couteau ou une paire de ciseaux, et vous choisissez votre outil en fonction du problème posé.
Programmeurs fonctionnels et orientés objet, arrêtez de traiter vos paradigmes comme une religion. Ce sont des outils, et ils ont tous leur utilité quelque part. Ce que vous utilisez ne devrait dépendre que des problèmes que vous résolvez.
La grande question : sommes-nous à l’aube d’une nouvelle révolution ?
En fin de compte, le débat – certes assez houleux – de la programmation fonctionnelle contre programmation orientée objet se résume à ceci : pourrions-nous être en train d’atteindre la fin de l’ère de la programmation orientée objet ?
De plus en plus de problèmes surviennent où la programmation fonctionnelle est souvent l’option la plus efficace. Pensez à l’analyse des données, à l’apprentissage automatique et à la programmation parallèle. Plus vous entrez dans ces domaines, plus vous aimerez la programmation fonctionnelle.
Mais si vous regardez le statu quo, il existe une douzaine d’offres pour les programmeurs orientés objet à une offre pour les codeurs fonctionnels. Cela ne veut pas dire que vous n’obtiendrez pas de travail si vous préférez ce dernier ; les développeurs fonctionnels sont encore assez rares de nos jours.
Le scénario le plus probable est que la programmation orientée objet perdurera encore une dizaine d’années. Bien sûr, l’avant-garde est fonctionnelle, mais cela ne signifie pas que vous devriez encore abandonner l’orientation objet. C’est toujours incroyablement bon d’avoir dans votre répertoire.
Alors ne jetez pas la programmation orientée objet hors de votre boîte à outils dans les prochaines années. Mais assurez-vous que ce n’est pas le seul outil dont vous disposez.
Cet article a été écrit par Rhéa Moutafis et a été publié à l’origine sur Vers la science des données. Vous pouvez le lire ici.