Programmation orientée objet
Un article de Wikipédia, l'encyclopédie libre.
La programmation orientée objet (POO) ou programmation par objet, a été élaborée par Alan Kay dans les années 1970. C'est un paradigme de programmation informatique qui consiste en la définition et l'interaction de briques logicielles appelées objets ; un objet représente un concept, une idée ou toute entité du monde physique, comme une voiture, une personne ou encore une page d'un livre. Il possède une structure interne et un comportement, et il sait communiquer avec ses pairs. Il s'agit donc de représenter ces objets et leurs relations ; la communication entre les objets via leur relation permet de réaliser les fonctionnalités attendues, de résoudre le ou les problèmes.
Orthogonalement à la programmation par objet, afin de faciliter le processus d'élaboration d'un programme, existent des méthodologies de développement logiciel objet dont la plus connue est USDP (Unified Software Development Process).
Il est possible de concevoir par objet une application informatique sans pour autant utiliser des outils dédiés. Il n'en demeure pas moins que ces derniers facilitent de beaucoup la conception, la maintenance, et la productivité. On en distingue plusieurs sortes :
- les langages de programmation (Java, C#, vala, Objective C, Eiffel, Python, C++, PHP, Smalltalk...)
- les outils de modélisation qui permettent de concevoir sous forme de schémas semi-formels la structure d'un programme (Objecteering, UMLDraw, Rhapsody…)
- les bus distribués (DCOM, CORBA, RMI, Pyro...)
- les ateliers de génie logiciels ou AGL (WinDev et son langage le WLangage)
Des langages à objets, il en existe actuellement deux catégories : les langages à classes et ceux à prototypes, que ceux-ci soient sous forme fonctionnelle (CLOS, OCaml…) ou impérative (C++, Java…) ou les deux (Python).
Sommaire |
[modifier] Origines
Le langage Simula-67, en implantant les Record Class de Hoare, pose les constructions qui seront celles des langages orienté objet à classes : classe, polymorphisme, héritage, etc. Mais c'est réellement par et avec Smalltalk 71 puis Smalltalk 80 (Dan Ingalls), inspiré en grande partie de Simula 67 et de Lisp, que les principes de la programmation par objets, résultat des travaux d'Alan Kay, sont véhiculés : objet, encapsulation, messages, typage et polymorphisme (via la sous-classification) ; les autres principes, comme l'héritage, sont soit dérivés de ceux-ci ou une implantation. Dans Smalltalk, tout est objet, même les classes. Il est aussi plus qu'un langage à objets, c'est un environnement graphique interactif complet.
À partir des années 1980, commence l'effervescence des langages à objets : Objective C (début des années 1980), C++ (C with classes) en 1983, Eiffel en 1984, Common Lisp Object System dans les années 1980, etc. Les années 1990 voient l'âge d'or de l'extension de la programmation par objet dans les différents secteurs du développement logiciel.
Depuis, la programmation par objet n'a cessé d'évoluer aussi bien dans son aspect théorique que pratique et différents métiers et discours mercatiques à son sujet ont vu le jour :
- l’analyse objet (AOO ou OOA en anglais),
- la conception objet (COO ou OOD en anglais),
- les bases de données objet (SGBDOO),
- les langages objets avec les langages à prototypes,
- ou encore la méthodologie avec MDA (Model Driven Architecture).
Aujourd’hui, la programmation par objet est vue davantage comme un paradigme, le paradigme objet, que comme une simple technique de programmation. C'est pourquoi, lorsque l'on parle de nos jours de programmation par objets, on désigne avant tout la partie codage d’un modèle à objets obtenu par AOO et COO.
[modifier] Les principes
La programmation orienté objet a été introduit par Alan Kay et a été véhiculée la première fois avec Smalltalk. Toutefois, ses principes n'ont pas été formalisées et c'est au cours des décennies 1980 et plus particulièrement 1990 qu'elles ont été théorisées. C'est ainsi que par exemple le typage de second ordre, qui qualifie le typage de la programmation orienté objet (appelé Duke Typing par la communauté Ruby), n'a été formulée qu'en 1995 par Cook.
[modifier] L'objet (attribut et méthodes)
Concrètement, un objet est une structure de données valuées et cachées et qui répond à un ensemble de messages. Cette structure de données définit son état tandis que l'ensemble des messages qu'il comprend décrit son comportement :
- Les données ou champs qui décrivent sa structure interne sont appelées ses attributs ;
- L'ensemble des messages forme ce que l'on appelle l'interface de l'objet ; c'est seulement au travers de celui-ci que les objets interagissent entre eux. La réponse à la réception d'un message par un objet est appelée une méthode (méthode de mise en œuvre du message) ; elle décrit comment le message doit être répondu.
Les attributs (ou plus exactement leur représentation informatique) et les méthodes sont cachés ; ils forment une boîte noire. C'est le principe d'encapsulation. Son avantage principal réside dans la capacité à pouvoir modifier la structure interne des objets ou les méthodes associées aux messages sans impacter les utilisateurs des objets.
Exemple d'un objet représentant un nombre complexe donné. Celui-ci peut aussi bien être représenté sous forme cartésienne (réel, imaginaire) que sous forme trigonométrique ou exponentielle (module, angle). Cette représentation, quelle qu'elle soit, est cachée et est interne à l'objet sous forme d'une structure de données. Toutefois, celui-ci peut proposer deux messages permettant chacun de lire une représentation différente du nombre complexe. En utilisant les seuls messages que comprend notre nombre complexe, les objets appelants sont assurés de ne pas être impactés lors d'un changement de sa structure interne. Cette dernière n'est accessible que par (et donc aussi au travers) les méthodes des messages.
[modifier] Le typage et le polymorphisme
Dans la programmation par objet, chaque objet est typé. Le type définit la syntaxe (comment l'appeler ?) et la sémantique (qu'est ce qu'il fait ?) des messages auxquels peut répondre un objet. Il correspond donc, à peu de chose près, à l'interface de l'objet. Toutefois, la plupart des langages objets ne proposent que la définition syntaxique d'un type (C++, Java, C#, ...) et rares sont ceux qui fournissent aussi la possibilité de définir aussi sa sémantique (Eiffel avec sa conception par contrats).
Un objet peut appartenir à plus d'un type. C'est ce que l'on appelle le polymorphisme. Ceci permet d'utiliser des objets de types différents là où est attendue un objet d'un type précis, dès que ceux-ci satisfont le type requis.
Une façon de réaliser le polymorphisme est le sous-typage (appelé aussi héritage de type) : mécanisme par lequel est raffiné un type père en un autre type, le sous-type ; c'est un mécanisme de restrictions des espaces de valeurs du type. Les objets de ce sous-type sont conformes aussi au type père, ces objets sont donc au moins de deux types. De ceci découle le principe de substitution de Liskov. Toutefois, le sous-typage est limité et ne permet pas de résoudre le problème des types récursifs (un message qui prend comme paramètre un objet du type de l'appelant). Pour résoudre ce problème, en 1995, Cook définit la sous-classification et avec lui le typage du second ordre qui régit la programmation orienté objet : le type est membre d'une famille polymorphique à point fixe de types (appelée classe). Les traits sont une façon de représenter explicitement les classes de types. (La représentation peut aussi être implicite comme avec Smalltalk, Ruby, etc.)
On distingue dans les langages objets trois mécanismes du typage :
- le typage dynamique : le type des objets est implicite et déterminé à l’exécution lors de la création des dits objets (Smalltalk, CLOS, Python, PHP…),
- le typage statique : le type des objets est spécifié explicitement par le développeur lors de leur déclaration (C++, Java, C#, Pascal…),
- le typage par inférence : le type est déterminé par le compilateur ou par l'interpréteur à partir du contexte, de l'expression dans laquelle il apparait (OCaml, Scala).
De même, deux mécanismes du sous-typage existent : l’héritage de type simple (Smalltalk, Java) et multiple (C++, Python, CLOS, Eiffel, WLangage).
Attention : le polymorphisme ne doit pas être confondu avec le sous-typage ou avec l’attachement dynamique (dynamic binding en anglais).
[modifier] La redéfinition
La programmation objet permet à un objet de raffiner la mise en œuvre d'un message défini pour des objets d'un type parent, autrement dit de redéfinir la méthode associée au message : c'est le principe de redéfinition des messages (ou overriding en anglais).
Or, dans une définition stricte du typage (typage du premier ordre), l'opération résultant d'un appel de message doit être la même quel que soit le type exact de l'objet référé. Ceci signifie donc que, dans le cas où l'objet référé est de type exact un sous-type du type considéré dans l'appel, seule la méthode du type père est exécutée :
Soit le type Reel = { plus: Reel x Reel -> Reel}
et son sous-type Entier <: Reel,
alors si i: Entier et r: Reel, l'appel
i plus r
conduit à l'exécution de la méthode associée au message plus dans le type Reel, et non à celle associée au même message dans le sous-type Entier dans le cas d'une redéfinition.
Pour réaliser alors la redéfinition, deux solutions existent :
- le typage du premier ordre associé à l'attachement dynamique (c'est le cas de C++, Java, C#, ...). Cette solution induit une faiblesse dans le typage et peut conduire à des erreurs. Les relations entre type sont définies par le sous-typage (théorie de Liskov) ;
- le typage du second ordre (duquel découlent naturellement le polymorphisme et l'appel de la bonne méthode en fonction du type exact de l'objet). Ceci est possible avec Smalltalk et Eiffel. Les relations entre types sont définies par la sous-classification (théorie F-Bound de Cook).
[modifier] Classe et prototype
La structure interne des objets et les messages auxquels ils répondent sont définis, explicités, par des modules logiciels. C'est aussi par ces mêmes modules que sont créés les objets, via des opérations dédiées. Deux représentations existent de ces modules : la classe et le prototype.
La classe est une structure informatique particulière dans le langage objet. Elle décrit la structure interne des données et elle définit les méthodes qui s'appliqueront aux objets de même famille (même classe) ou type. Elle propose des méthodes de création des objets dont la représentation sera donc celle donnée par la classe génératrice. Les objets sont dits alors instances de la classe. C'est pourquoi les attributs d'un objet sont aussi appelés variables d'instance et les messages opération d'instance ou encore méthodes d'instance. L'interface de la classe (l'ensemble des opérations visibles) forme les types des objets. Selon le langage de programmation, une classe est soit considérée comme une structure particulière du langage, soit elle même comme un objet (objet non-terminal). Dans ce dernier cas, la classe a besoin elle aussi d'être créée et définie par une classe : ce sont les méta-classes. L'introspection des objets ou la méta-programmation est définie alors dans ces méta-classes. Sinon, elle est définie dans le runtime au travers des objets ou des classes. La classe peut donc aussi être décrite par des attributs et des messages. Ces derniers sont alors appelés, par opposition aux attributs et messages d'un objet, variables de classe et opération ou méthodes de classe. Parmi les langages à classes on retrouve Smalltalk, C++, C#, Java, etc.
Le prototype est un objet à part entière et qui sert de prototype de définition de la structure interne et des messages. De ce prototype, par le mécanisme de clonage, sont créés les autres objets de mêmes types. Dans ce modèle, il n'y a plus de distinction entre attributs et messages : ce sont tous des slots. Un slot est un label de l'objet, privé ou public, auquel est attachée une définition : ce peut être une valeur ou une opération. Cet attachement peut être modifié à l'exécution. Chaque modification d'un slot est locale à l'objet concerné et n'impacte pas ses clones. Chaque ajout d'un slot impacte l'objet et l'ensemble de ses clones. Pour modifier globalement un slot, autrement dit pour avoir un impact sur l'objet et tous ses clones, le concept de trait a été introduit. Un trait est un ensemble d'opérations de même catégorie (clonage, persistance, etc.) transverse aux objets. Il peut être représenté soit comme une structure particulière du langage, comme un slot dédié ou encore comme un prototype. L'association d'un objet à un trait fait que l'objet et ses clones sont capables de répondre à toutes les opérations du trait. Un objet est toujours associé à au moins un trait, et les traits sont les parents des objets (selon une relation d'héritage). Un trait est donc un mixin doté d'une parenté. Parmi les langages à prototype on trouve Javascript, Self, Io, Slater, Lisaac, etc.
Voir l'article dédié : Programmation orientée prototype.
[modifier] Modélisation objet
La modélisation objet consiste à créer un modèle informatique du système de l’utilisateur (un système informatique). Ce modèle peut rassembler aussi bien des éléments du monde réel que des concepts ou des idées propres au métier ou au domaine duquel fera partie le système. La modélisation Objet consiste à définir, à qualifier dans un premier temps ces éléments sous forme de types, donc indépendamment de la mise en œuvre. C’est ce que l’on appelle l'analyse orientée objet ou OOA (Object-Oriented Analysis).
Puis, on propose une ou des solutions techniques pour représenter les éléments définis dans le système informatique. C’est ce que l’on appelle la conception orientée objet ou OOD (Object-Oriented Design). Une fois un modèle de conception établi, il est possible au développeur de leur donner corps dans un langage de programmation. C’est ce que l’on appelle la programmation orientée objet ou OOP (Object-Oriented Programming). À un modèle d’analyse peuvent correspondre plusieurs modèles de conception.
Pour écrire ces différents modèles, différents langages et méthodes ont été mis au point, dont OMT de Rumbaugh, BOOCH'93 de Booch et OOSE de Jacobson. Toutefois, ces méthodes ne permettaient de modéliser que certains types d’applications et se trouvaient limitées dans d’autres contextes. La méthode OMT prévalait sur l’ensemble des autres méthodes dans la première partie de la décennie 1990.
À partir de 1994, Rumbaugh, Booch et Jacobson ont décidé de s’unir dans l’élaboration d’une nouvelle méthode, suffisamment générique, pour pouvoir s’appliquer à quasiment tous les contextes applicatifs. Ils ont commencé d’abord par définir un langage de modélisation fortement inspiré de celles des méthodes des trois auteurs : UML (Unified Modeling Language). Une fois celui-ci pris en charge par l’OMG (Object Management Group), un organisme destiné à standardiser des technologies objet, comme CORBA (Common Object Request Broker Architecture), un intergiciel (middleware en anglais) objet réparti, Rumbaugh, Booch et Jacobson se sont attaqués à la méthode proprement dite: USDP (Unified Software Development Process). Cette méthode définit un cadre générique de développement objet avec UML comme langage de modélisation. USDP (généralement raccourci en UP) est une méthode itérative et incrémentale, centrée sur l’architecture et guidée par les cas d’utilisation et la réduction des risques. C’est aux concepteurs de s’attribuer cette méthode en l’instanciant à leur métier et à leur domaine.
Néanmoins pour un certain nombre de concepteurs objet, dont Bertrand Meyer, l’inventeur du langage orienté objet Eiffel, guider une modélisation objet par des cas d’utilisations est une erreur de méthode qui n’a rien d’objet et qui est plus proche d’une méthode fonctionnelle. Pour eux, les cas d’utilisations sont relégués à des utilisations plutôt annexes comme la validation d’un modèle par exemple.
[modifier] Bibliographie
- Brad J. Cox, Andrew J. Novobilski (1986). Object-Oriented Programming: An Evolutionary Approach, ISBN 0201548348.
- Bertrand Meyer (2000). Conception et programmation orientées objet, ISBN 2-212-09111-7.
- Grady Booch, James Rumbaugh, Ivar Jacobson (2000). Le guide de l’utilisateur UML, ISBN 2-2120-9103-6
- De Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1999). Design Patterns, ISBN 2-7117-8644-7.
- De Hugues Bersini (2007). L'Orienté Objet, ISBN 978-2-212-12084-4.
[modifier] Articles connexes
[modifier] Liens externes
- Introduction à la POO Apprendre simplement la Programmation Orientée Objet
- Des paradigmes "classiques" à l'orienté objet
- Analyse et conception orientée objet avec UML et RUP, un survol rapide.
- (en) The Theory of Classification de Anthony J.H. Simons sur le JOT (Journal of Object Technology)