Penser orienté-objet
Orienté objet est une formule accrocheuse. Appeler n’importe quoi « orienté objet » vous donne l’air intelligent. Ruby se proclame un langage de script orienté objet, mais que veut dire au juste « orienté objet » ?
Il y a eu des tas de réponses à cette question, qui se ramènent probablement à la même chose. Plutôt que résumer le tout à la hâte, réfléchissons un instant au paradigme de la programmation traditionnelle.
Classiquement, un problème de programmation est attaqué à l’aide de structures de données, et de procédures qui manipulent ces données. Dans ce modèle, les données sont inertes, passives, et, sans défense, elles gisent à la merci du corps du programme qui est actif, logique et tout-puissant.
Le problème avec cette approche est que les programmes sont écrits par des programmeurs, qui ne sont que des humains, et ne peuvent avoir clairement en tête à un instant donné qu’une quantité limitée de détails. Au fur et à mesure qu’un projet grossit, son noyau procédural grossit jusqu’au point où il est difficile de se rappeler comment le tout marche. De petits trous de mémoire et des fautes de frappe tournent en bugs bien cachés. Des intéractions complexes et imprévues commencent à apparaitre dans le noyau procédural, et le maintenir commence à ressembler à la tentative de maîtriser un gros calmar en colère sans qu’un tentacule ne vous touche la figure. Il y a des règles de programmation qui vous aident à minimiser et à localiser les bugs dans cette approche classique, mais il y a une meilleure solution, qui impose cependant de changer radicalement notre façon de travailler.
Ce que la programmation orientée objet fait est qu’elle nous laisse déléguer une grande partie du travail fastidieux et répétitif aux données elles-mêmes, ce qui change le statut de la donnée de passive en active. Autrement dit,
- on arrête de traiter les données comme des boites au couvercle grand ouvert, qui nous laissent les atteindre et en faire n’importe quoi ;
- on commence à traiter les données comme des machines avec un capot bien fermé, des boutons et des écrans de commande et de contrôle.
Ce que nous appelons ci-dessus « machine » peut être très simple ou très complexe à l’intérieur; on ne peut pas le savoir de l’extérieur, et nous nous interdirons d’ouvrir la machine (sauf quand nous sommes absolument sûrs que quelque chose ne va pas dedans), si bien que nous devons agir dessus par l’intermédiaire des boutons et des écrans. Une fois que la machine est construite, nous n’avons plus à penser à la façon dont elle fonctionne.
Vous pensez peut-être que nous ne faisons que nous rajouter du travail, mais cette approche tend à s’avérer une bonne façon de prévenir toutes sortes d’ennuis.
Commençons par un exemple trop simple pour être d’intérêt pratique, mais qui illustrera au moins une partie du concept. Votre voiture a un compteur journalier. Son rôle est de garder trace de la distance parcourue depuis la dernière fois qu’il a été réinitialisé par une pression sur le bouton. Comment modéliserions nous cela dans un langage de programmation ? En C, le compteur journalier serait juste une variable numérique, peut-être du type float. Le programme manipulerait cette variable en augmentant sa valeur de petits incréments, avec des remises à zéro quand il faudrait. Qu’est-ce qui ne va pas là-dedans ? Un bug dans le programme pourrait affecter — pour quelque raison imprévue — une valeur incorrecte à la variable. Quiconque a programmé en C sait ce que c’est que de passer des heures ou des jours à traquer ce genre de bug dont la cause semble ridiculement simple une fois qu’on l’a trouvée (le moment de la découverte se manifeste généralement par le bruit d’une forte tape sur le front).
Le même problème serait attaqué sous un tout autre angle dans un contexte orienté objet. La première chose qu’un programmeur demande alors quand il définit le compteur journalier n’est pas « Quel est le type de donnée qui ressemble le plus à ce truc ? » mais « Comment ce truc est-il sensé agir ? » La différence s’avère profonde. Il est nécessaire de passer un peu de temps à décider exactement ce qu’un compteur est, et comment le monde extérieur s’ attend à interagir avec lui. Nous décidons de construire une petite machine qui contrôle ce qui nous permettra de l’incrémenter, de le réinitialiser, de lire sa valeur, et rien d’autre.
Nous ne fournirons pas de moyen d’assigner au compteur une valeur quelconque. Pourquoi ? Parce que nous savons tous que les compteurs ne marchent pas comme ça. Il n’y a que peu de choses qu’on peut faire à un compteur, et ce sont celles-là que nous permettrons. Alors, si quelque chose d’autre dans le programme essaie à tort de placer une autre valeur (par exemple, la température voulue pour la climatisation de la voiture) dans le compteur, eh bien il y aura aussitôt une indication que cela ne va pas. On nous dira à l’exécution du programme (ou peut-être dès la compilation, cela dépend des langages) que nous n’avons pas le droit de mettre une valeur dans les objets de type Compteur. Le message ne sera peut-être pas aussi clair que ça, mais il s’en rapprochera plus ou moins. Cela n’évitera pas l’erreur, à vrai dire. Mais cela nous en indiquera rapidement la direction. Voici une des nombreuses choses qui font que la programmation orientée objet peut nous faire gagner beaucoup de temps.
On va généralement au-delà dans l’abstraction, parce qu’il s’avère qu’il est aussi facile de fabriquer des usines à faire des machines que de faire les machines elles-mêmes. Il est donc vraisemblable qu’au lieu de fabriquer directement un compteur, on va plutôt s’arranger pour pouvoir dériver toutes sortes de compteurs d’un seul cadre général. Ce cadre — si on veut, l’usine à compteurs — est ce qu’on appelle une classe, et les compteurs individuels générés d’après ce modèle général (fabriqués dans l’usine) sont ce qu’on appelle les objets. La plupart des langages orientés objet imposent qu’une classe soit définie avant qu’on puisse créer des objets d’une nouvelle sorte, mais ça n’est pas le cas de ruby.
Ici, il convient de noter que l’usage d’un langage orienté objet n’a pas automatiquement pour effet une bonne conception orientée objet. A vrai dire il est possible en n’importe quel langage d’écrire du code peu clair, mal fichu, buggé et instable. Ce que ruby fait pour vous, (au contraire de C++ en particulier) est de rendre la pratique de la programmation orientée objet assez naturelle pour que, même quand vous travaillez sur de petites choses, vous ne ressentiez pas le besoin de recourir à du code hideux pour éviter des efforts. On discutera de la façon dont ruby produit ce merveilleux résultat en avançant dans ce guide ; le prochain sujet étant relatif au « boutons et cadrans » (les méthodes) et de là nous passerons aux “usines” (les classes). Vous êtes encore là ?
Guide de l’utilisateur Précédent : GDU : Itérateurs Suivant : GDU : Méthodes

