points de vue

les déambulations d'un codeur

Aller au contenu | Aller au menu | Aller à la recherche

Les Programmations Orientées Objet

La Programmation Orienté Objet (POO pour les intimes) est de nos jours la lingua franca de la programmation dite impérative. Pourtant, fort est de constater que celle couramment usitée est loin de l’approche définie par son auteur, Alan Kay, au point que l’on peut dire qu’il existe actuellement en fait deux approches orientées objet !

En quoi les deux approches diffèrent elles ? On pourrait résumer cette différence par ceci :

  • dans l’approche la plus pratiquée, l’accent du développement est mis sur la structuration des objets avec une volonté d’encapsuler leur état. Débutant avec Simula 67, elle est de nos jours commune et véhiculée par des langages comme C++ ou Java.
  • dans l’approche originale, l’accent est au contraire mis sur le comportement des objets, caractérisé par l’ensemble des messages qu’ils comprennent, la structure de ceux-ci et leur changement d’état étant encapsulé ; la structuration est reléguée au second rang. Initiée avec Smalltalk, elle est véhiculée de nos jours par des langages comme Smalltalk, Io ou Ruby.

D’où vient cette différence ? Une explication peut être donnée avec ces propos d’Alan Kay :

The “official” computer science world started to regard Simula as a possible vehicle for defining abstract data types (even by one of its inventors), and it formed much of the later backbone of ADA. […] Instead, the objects should be presented as site of higher level behaviors more appropriate for use as dynamic components.

A l’origine, les concepteurs de Smalltalk et pères de la POO se sont inspirés des techniques utilisées dans d’autres langages (Lisp, Sketchpad, JOSS, …) pour implémenter les concepts sous-jacents à la POO, et parmi ceux-là les constructions utilisées dans Simula les ont fortement inspirés. A cette époque, on travaillait sur un moyen de structurer le code à l’aide de types abstraits de données. C’est dans ce contexte que certains, comme Ole-Johan Dahl et Kristen Nygaard, les auteurs de Simula, ne virent de la POO que comme une meilleure structuration du code à l’aide de constructions idoines : classes de données, instanciation, sous-typage, attachement dynamique (ces deux derniers permettant le polymorphisme), etc. C’est sous cette tendance qu’ont émergés d’autres langages comme Pascal Object ou C++ qui, à leur tour, ont influencé plusieurs générations de développeurs. C’est ainsi que, pour beaucoup, la POO se résume à écrire des structures (les classes) regroupant à la fois les champs (appelés attributs) et les opérations (appelées méthodes), dont l’interface (les parties accessibles depuis l’extérieur) forme le type ; cette approche est plutôt une programmation orientée classe dans laquelle la classe fusionne le concept de module avec celui de type.

Pour mieux comprendre les concepts sous-jacents à la POO et ce qu’avaient en tête son auteur, je recommande fortement la lecture de l’histoire de Smalltalk. On peut y lire, entre autre, que la POO est née de plusieurs idées, dont l’une d’elle peut être résumée par ces propos de Bob Barton :

The basic principal of recursive design is to make the parts have the same power as the whole.

Et dans leur vision, les objets sont perçus comme des cellules d’un organisme cellulaire ou des petits ordinateurs dans un réseau par le biais duquel ils interagissent par messages.

Plus concrètement, dans cet échange de mails, Alan Kay expliquent ce qu’est la POO :

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

Alan Kay ne parle aucunement de structuration. Il met en avant d’abord les concepts de messages, puis d‘encapuslation à la fois des effets de bords et des détails techniques et d’implémentation, et de … résolution retardée ! L’expression extreme late-binding of all things peut surprendre mais est essentielle pour plusieurs choses dont le polymorphisme. Pour comprendre ceci, il faut d’abord savoir qu’en POO le concept de message est décoléré de celui de méthode : une méthode est la réponse propre à l’objet à un message donné (autrement dit son code d’implémentation). Ensuite, tout objet qui sait répondre à un message, quelque soit son type, doit pouvoir être utilisé comme receveur. Donc, la méthode à exécuter en réponse à un message ne peut être connue que lorsque le type réel de l’objet est identifié, ce qui ne peut se faire que tardivement, c’est-à-dire lorsque celui-ci est effectivement inféré (le message est transmis à l’objet). Ceci permet à un objet dont le type satisfait l’interface attendue d’être utilisé en tant que receveur et ceci sans nécessairement entrer dans une relation de sous-typage avec l’interface ; celle-ci peut d’ailleurs être dynamique au sens où elle définit uniquement les messages attendus dans un contexte donné. On dit alors que le type de l’objet est un élément de la classe de types représentée par cette interface ; autrement dit il satisfait l’interface définie par la classe de types. En effet, en POO, le polymorphisme provient d’un typage d’ordre supérieur et des relations de sous-classement (les classes ici en tant qu’ensembles polymorphiques de types, cf. le Polymorphisme F-Bound de William Cook et al. ou les articles de Anthony J.H. Simons sur la théorie de la classification dans le JOT, Journal Of Object Technology) ; les relations entre objets ne reposent plus sur le type des objets mais sur l’appartenance de leur type à des classes de types. (Ce que les développeurs Ruby appellent, in vulgare sermo, le duke typing.) Les langages comme C++, Java ou C#, quant à eux, ont rejeté le concept de message, et réalisent le polymorphisme par des relations de sous-typage (principe de Liskov) et par la surcharge de méthodes, mais celui-ci offre des possibilités en deçà de celui de la POO ; c’est pourquoi, pour étendre les possibilités polymorphiques, ils permettent, via la généricité, et dans une certaine mesure, un typage de second ordre (quantification universelle ou, mieux, contrainte). Voici un exemple de type de second ordre contraint en Java :

public class Persistable<T extends Persistable<T>> {
  ...
}

Je conclurai brièvement cette article avec cette phrase du GOOS (Growing Object-Oriented Software) :

Your domain model is not in the classes you create in your source code, but in the messages that the objects pass to one another when they communicate at runtime.

Miguel Moquillon

Auteur: Miguel Moquillon

Restez au courant de l'actualité et abonnez-vous au Flux RSS de cette catégorie

Commentaires (0)

Soyez le premier à réagir sur cet article

Ajouter un commentaire Fil des commentaires de ce billet

no attachment



À voir également

Exemple d'immutabilité en Java

Dans un billet précédent sur l’égalité d’identité et celle de valeurs, je vous ai parlé d’objets immuables pour lesquels l’égalité de valeur et l’égalité d’identité se confondent. J’ai souvent vu dans divers blogues sur l’immutabilité en Java l’utilisation du mot clé final. J’ai toujours trouvé son usage pour réaliser l’immutabilité comme absurde et surtout par trop contraignant. Pour moi, il ne sert à rien de qualifier les propriétés des objets comme final étant donné que celles-ci doivent être encapsulées selon les principes de la programmation orienté objet. Non, l’immutabilité des objets devrait au contraire se faire au niveau du comportement et surtout des mutateurs de ces objets.

Lire la suite

Une histoire d'objets obèses ...

Il arrive dans un projet en Java de se trouver, selon le métier ou le domaine adressé, avec des classes d’objets obèses en méthodes qu’elles soient publics ou propres aux objets de la classe. Or, sachant que l’on passe plus de temps à lire, voir à toucher du code existant qu’à en écrire de nouveaux, ceci peut vite devenir pénible. Evidemment, avec nos IDE actuels, il est facile de naviguer entre les différentes méthodes et propriétés d’une classe. Mais en général ceci signifie que l’on sait, déjà, à peu près ce que l’on cherche ou que l’on connait a minima les responsabilités ou certaines particularités d’implémentation de la classe. Lorsqu’on doit toucher du code inconnu ou au mieux revenir sur du code au bout de 6 mois, nous aimons bien identifier aisément les parties à utiliser ou à retoucher et accéder à l’essentiel sans se perdre dans les méandres de la ou des classes inspectées. En effet, il peut être difficile, avec de telles classes, de démêler le comportement de l’objet, ce qui le caractérise, du reste. En tout cas c’est mon cas. Pour éviter de tels embonpoints, je vous propose d’utiliser les approches de certains langages fonctionnels comme Haskell (ou OCaml), dans lesquels les types et les fonctions sur ces types sont séparés (au sein d’un même module tout de même).

Lire la suite