Page de Nicolas Georgehttp://www.eleves.ens.fr/home/george/info/anti_objets.html

Contre l'orienté objets

Table des matières

Je ne vais pas ici m'acharner à démolir la programmation orientée objets. Je vais juste évoquer en quoi ce n'est pas une panacée, et notamment souligner dans quels cas il s'agit d'une solution trop élaborée.

Ce que l'orienté objets permet

Beaucoup font de la programmation orientée objets pour atteindre un but nettement plus simple. Les langages soi-disant orientés objets ont connu une grande mode, et leur utilisation a conduit à mélanger un certain nombre de notions différentes.

Compartimenter le programme

Un des atouts presque toujours mis en avant pour prôner la programmation orientée objets, c'est qu'elle offre la possibilité de compartimenter le programme en entités bien séparées, communiquant uniquement par un protocole bien spécifié, ce qui permet de les développer séparément, et de changer radicalement l'intérieur de l'une sans casser tout le projet. Ceci est vrai. Mais ça n'a pas de rapport avec l'orientation objets. C'est en fait la notion qu'on appelle modularité : chaque partie du programme constitue un module, qui exporte uniquement une interface bien définie, et est indépendante du fonctionnement des autres.

Un langage peut proposer une gestion syntaxique des modules ; beaucoup le font, de manière plus ou moins forte (au sens où ils incitent plus ou moins le programmeur à l'utiliser, et offrent plus ou moins de moyens pour contourner le découpage.

Les langages spécifiquement orientés objets n'ont pas (en général) de support pour la modularité, car c'est la structure par objets qui en tient lieu. Dans ce cas, un module sera implémenté comme une classe, dont on ne créera qu'une instance. Toute la structure des modules est là, mais avec des choses en plus.

Un dernier mot sur la modularité, pour dire qu'elle n'est, en l'absence de support syntaxique dans le langage, qu'affaire d'auto-discipline des programmeurs.

Choisir dynamiquement parmi des catalogues de fonctions

Une spécificité de la programmation orientée objets est la notion de classes dérivées, qui peuvent s'utiliser en lieu et place de la classe parente. Cette propriété est souvent utilisée pour permettre à une partie du programme d'appeler sélectivement différents jeu de fonctions, à l'interface identique, mais à la fonction différente (comme afficher sur l'écran/imprimer).

Pour ce faire, on définit une classe abstraite déclarant les différentes fonctions, et on implémente les différents jeux de fonctions dans des classes dérivées. Il ne reste plus qu'à créer une instance de la classe implémentant les fonctions qu'on veut effectivement utiliser, et la faire passer pour un exemplaire de la classe générique.

Ici encore, ce n'est qu'une partie de la structure d'objets qui est utilisée. En effet, on n'utilise ici qu'une instance de chaque classe au plus à chaque instant. Dans ce cas, c'est que ce n'est pas un objet qu'on est en train de considérer, puisque par définition, un objet est non unique.

Utiliser des fonctions dans des environnements différents

Enfin, on exploite la structure par objets ! En effet, la particularité des objets, c'est d'avoir des méthodes, qui sont des fonctions dont le fonctionnement change selon l'objet sur lequel elles sont appelées (implicitement).

Il est vrai que, s'il ne s'agit que de passer le premier paramètre à la fonction en préfixe et non pas comme les autres, les objets ne servent à rien. Mais cette possibilité, combinée à la précédente, constitue la véritable utilisation des objets.

Encadrer un système de typage arborescent

La structure en classes et sous classes permet l'utilisation d'un système de types avec des inclusions. Ceci tient au fait que les objets dérivant d'une même classe ont une structure similaire (ils possèdent au moins toute l'information de la classe dont ils dérivent), ou se ramène au choix dynamique d'un catalogue.

Le polymorphisme

Certains langages orientés objets offrent des possibilités pour construire des fonctions génériques sur des objets en sachant uniquement qu'ils auront certaines propriétés. Hélas, dans le seul cas où j'ai pu constater de visu, ce polymorphisme était on ne plus mal géré. En effet, il se traduisant par la re-compilation de la fonction polymorphe pour chaque classe sur laquelle on voulait la faire travailler.

Le problème, c'est que ce polymorphisme ne relève pas de la structure d'objets. Or quand un langage ne connaît que ce type de structure, forcément, il la met à toutes les sauces, pas toujours de manière maîtrisée.

Produire du code réutilisable

Ah, la belle légende ! Comme si adopter une structure de programme, une manière de présenter un problème, allait influer sur la propreté du code ! La réutilisabilité, c'est avant tout une question de propreté, de discipline, de prévoyance. D'ailleurs, «réutilisabilité», ça ne veut en soi pas dire grand-chose. S'il s'agit de réutiliser un bout de code faisant une tâche générique spécifique pour des applications très différentes, c'est la modularité qui entre en jeu. S'il s'agit de réutiliser un gros bout en en changeant quelques aspects, comme par exemple une boîte de dialogue, eh bien, selon ce qu'il y a à modifier, on peut craindre que ce ne soit hélas pas possible, même avec le code le plus orienté objets qui soit, ou même le plus proprement écrit qui soit.

Enfin, ceci est vrai si on s'interdit la modification du code lui-même. Car la réutilisabilité, en pratique, c'est principalement, et même presque uniquement, une question de licence, et de disponibilité de l'information (dont le code du programme). Et ça, ça ne dépend certainement pas du langage...

De la lisibilité

Le fait d'organiser totalement un programme comme un objet a un effet pervers : ça nuit fortement à la lisibilité du programme. En effet, la structure orientée objets demande beaucoup de travail implicite de la part du compilateur. Quand quelqu'un veut lire le programme, il doit faire lui-même ce travail. Ceci exige de garder dans sa tête le type des objets (car les noms ne permettent pas toujours de se le rappeler), de ne pas rater d'appel de fonction implicite (caché dans la création d'un objet, par exemple), de bien identifier quelle est la méthode qui va être appelée.

Un exemple typique, c'est l'utilisation de fonctions pour fixer et lire une variable d'instance. D'un côté, on pourrait la fixer directement à la main, mais si on décide de changer l'implémentation, il faut tout modifier parce qu'on aura peut-être besoin de faire quelque-chose à ce moment précis (note : un changement d'implémentation exige souvent de radicaux changements, celui-ci serait probablement négligeable) ; d'un autre côté, effectuer une opération non triviale à cet endroit inattendu rend le code plus difficile à lire.

Ceci n'est pas inévitable, mais ça exige un effort de réflexion sur la nécessité dans chaque cas. Il n'y a pas de règle simple pour dire dans quel cas il faut encapsuler et dans quel cas il ne faut pas, et surtout pas de recette miracle : «toujours faire ainsi». Bref, il faut se servir de son cerveau.

Conclusion

La programmation orientée objets est clairement une bonne chose dans de nombreux cas. On citera le plus classique, l'arborescence d'objets graphiques. Mais il faut savoir l'utiliser à bon escient : ne pas l'utiliser quand elle ne s'y prête pas, utiliser des outils plus spécifiques. En effet, si on considère la modularité par exemple, l'utilisation d'objets pour l'émuler introduit d'une part tous les inconvénients (qui ici sont assez peu sensibles), et est surtout très sous-optimale.

Bref, il faut savoir utiliser avec discernement l'orientation objets, ne pas la mettre à toutes les sauces, et savoir également utiliser les autres méthodes quand elles s'imposent.


Note finale : ce que j'ai écrit ici est ma propre conception des choses. Si vous avez des remarques, faites-moi en part.