La programmation orientée objet, souvent abrégée POO, est devenue si familière qu’il est facile d’oublier qu’elle correspond à une évolution historique précise de la manière de concevoir les logiciels. Classes, objets, méthodes, héritage, interfaces et polymorphisme font aujourd’hui partie du vocabulaire courant. Pourtant, ces notions ne sont pas apparues ensemble, ni dans le but de créer une recette universelle.
L’orientation objet est née d’un problème concret : comment représenter dans un programme des entités qui possèdent à la fois un état, un comportement et des interactions avec d’autres entités ?
De Simula à Smalltalk, puis de C++ à Java, Python et JavaScript, la réponse a profondément changé. L’histoire de la POO est celle d’une famille d’idées : rapprocher données et comportements, protéger certains détails, envoyer des messages, substituer des objets derrière un contrat commun et construire des abstractions réutilisables.
Avant les objets : procédures, données et complexité croissante
Les langages de haut niveau des années 1950 et 1960 apportent fonctions, procédures, structures de données et modules. Mais à mesure que les systèmes grandissent, les données représentant une même entité peuvent être manipulées par de nombreuses fonctions dispersées. Modifier leur représentation interne devient coûteux.
C’est dans la simulation qu’apparaît l’une des réponses les plus influentes.
Simula : représenter les acteurs d’une simulation
Dans les années 1960, les informaticiens norvégiens Ole-Johan Dahl et Kristen Nygaard travaillent sur des langages de simulation. Clients, machines, véhicules ou files d’attente possèdent chacun un état et évoluent au cours du temps.
Avec Simula 67, ils introduisent des mécanismes qui deviendront fondamentaux : classes, objets, héritage et méthodes virtuelles. Une classe décrit une catégorie d’entités ; un objet représente une instance particulière.
Le programme peut ainsi être organisé autour des acteurs du domaine plutôt qu’autour d’une simple succession de procédures.
Classe et objet : deux notions à distinguer
Dans le modèle classique, une classe définit une structure et des comportements communs. Un objet est une instance concrète créée à partir de cette définition.
Une classe Voiture peut définir vitesse, carburant et opérations comme accélérer ou freiner. Deux objets issus de cette classe partagent l’organisation générale mais conservent chacun leur état.
Cette distinction devient centrale dans C++, Java ou C#. Elle n’est pourtant pas universelle : certains systèmes reposent sur des prototypes et certains langages manipulent les classes elles-mêmes comme des objets.
Alan Kay et une autre vision de l’objet
À la fin des années 1960 et au début des années 1970, Alan Kay développe une autre vision, détaillée dans notre biographie d’Alan Kay.
Pour Kay, l’essentiel n’est pas d’abord la classe ou l’héritage, mais des objets autonomes qui échangent des messages. Chaque objet protège son état interne et décide comment répondre aux messages reçus.
Smalltalk : tout devient objet
Au Xerox PARC, Alan Kay, Dan Ingalls, Adele Goldberg et d’autres chercheurs développent Smalltalk. Les nombres, collections et classes participent au modèle objet ; l’interaction repose sur l’envoi de messages.
Smalltalk propose aussi un environnement interactif complet où le programme peut être exploré et modifié. Cette association entre langage, interface graphique et outils de développement influencera durablement l’informatique personnelle et les IDE.
L’objet comme état, identité et comportement
Un objet combine généralement trois dimensions : état, comportement et identité. Deux comptes bancaires peuvent avoir le même solde sans être le même compte.
Cette identité est utile dans certains domaines ; ailleurs, des valeurs immuables et des fonctions sont plus adaptées. La POO est donc un modèle puissant pour certaines catégories de problèmes, pas une obligation universelle.
Encapsulation et abstraction
L’encapsulation regroupe un état avec les opérations qui le manipulent et limite l’accès à certains détails internes. Un compte bancaire dont le solde ne peut être modifié qu’à travers des opérations contrôlées peut préserver ses invariants.
Elle ne se réduit pas à déclarer des champs private : son objectif est de réduire les dépendances et de permettre à une abstraction d’évoluer sans casser ses utilisateurs.
L’abstraction expose ce qu’un composant sait faire sans imposer la connaissance de son fonctionnement interne. Un objet Fichier peut proposer lire, écrire et fermer sans révéler les appels système sous-jacents.
La POO n’a pas inventé l’abstraction, mais elle en a popularisé une forme centrée sur des entités possédant état et comportement.
Héritage : utile, mais pas universel
L’héritage permet à une classe de reprendre et spécialiser les caractéristiques d’une autre. Une classe Vehicule peut servir de base à Voiture ou Moto.
Longtemps présenté comme un avantage majeur de la POO, il facilite certaines factorisations mais crée aussi un couplage fort entre classes de base et dérivées. Une hiérarchie profonde peut devenir rigide et difficile à modifier.
L’expérience a donc conduit à considérer l’héritage comme un outil parmi d’autres, et non comme le mécanisme automatique de réutilisation.
Polymorphisme : plusieurs objets, un même contrat
Le polymorphisme permet à un même code de travailler avec plusieurs types d’objets tant qu’ils respectent un comportement attendu. Une fonction peut dessiner cercle, rectangle ou triangle à travers une opération commune dessiner().
Selon les langages, ce polymorphisme passe par l’héritage, des interfaces, protocoles, le duck typing ou des mécanismes génériques. L’idée essentielle est de programmer contre un contrat plutôt que contre une implémentation précise.
Les méthodes virtuelles et la liaison dynamique permettent notamment de choisir à l’exécution l’implémentation correspondant au type réel d’un objet.
C++ : l’objet rencontre la programmation système
À partir de 1979, Bjarne Stroustrup cherche à combiner les abstractions de Simula avec l’efficacité de C. Le projet devient C++.
C++ diffuse massivement classes, constructeurs, destructeurs, héritage et fonctions virtuelles dans l’industrie, tout en conservant la programmation procédurale et en développant la programmation générique. Son succès montre déjà que l’objet peut coexister avec d’autres paradigmes.
Objective-C : envoyer des messages au-dessus de C
Créé au début des années 1980 par Brad Cox et Tom Love, Objective-C combine C avec un modèle dynamique inspiré de Smalltalk. Il devient important chez NeXT puis Apple, avant la montée de Swift.
C++ et Objective-C illustrent ainsi deux interprétations différentes de l’objet : abstractions compilées et contrôle des ressources d’un côté, envoi dynamique de messages de l’autre.
Les années 1990 : l’orientation objet devient dominante
Dans les années 1990, la POO devient un modèle dominant. Les interfaces graphiques s’y prêtent bien : fenêtres, boutons, menus et événements peuvent être représentés par des entités possédant état et comportement.
Analyse orientée objet, design patterns et UML se diffusent. La POO est parfois présentée comme une manière générale de concevoir tout système, ce qui produira à la fois des pratiques durables et certains excès.
Java, C# et la diffusion industrielle
En 1995, Java place historiquement les classes au centre, ajoute la gestion automatique de la mémoire et privilégie les interfaces plutôt que l’héritage multiple de classes. Notre article sur l’histoire de Java et la biographie de James Gosling détaillent cette évolution.
Pour toute une génération, apprendre à programmer devient presque synonyme d’apprendre classes et objets.
C# reprend ensuite de nombreuses idées familières à Java et C++ dans l’écosystème .NET, avant d’intégrer generics, lambdas, LINQ, records et pattern matching. Cette évolution vers le multiparadigme montre que même les langages fortement associés à la POO n’en font plus l’unique abstraction.
Python et JavaScript : d’autres modèles d’objet
Python prend en charge classes, héritage et polymorphisme sans imposer les classes partout. Les fonctions sont des valeurs et le duck typing privilégie souvent les comportements disponibles plutôt qu’une hiérarchie précise.
JavaScript, créé par Brendan Eich, repose historiquement sur des prototypes. La syntaxe moderne class s’appuie toujours sur ce mécanisme sous-jacent.
Ces deux langages rappellent que l’orientation objet ne se réduit pas aux hiérarchies de classes classiques.
Composition plutôt qu’héritage
Avec l’expérience, la recommandation préférer la composition à l’héritage s’est largement diffusée. Une voiture contient un moteur plutôt que d’en hériter.
La composition réduit souvent le couplage et facilite le remplacement d’un composant. Elle ne rend pas l’héritage inutile : elle rappelle simplement que la réutilisation ne nécessite pas toujours une arborescence de classes.
Interfaces, SOLID et principes de conception
Les interfaces permettent de séparer ce dont un composant a besoin de l’implémentation exacte qui satisfait ce besoin. Une classe peut dépendre d’un contrat Stockage dont plusieurs implémentations existent.
Cette idée alimente l’injection de dépendances et plusieurs principes SOLID. Ces principes - responsabilité unique, ouvert/fermé, substitution de Liskov, ségrégation des interfaces et inversion des dépendances - ont beaucoup influencé l’industrie.
Ils restent toutefois des heuristiques. Les appliquer mécaniquement peut multiplier classes, interfaces et couches sans bénéfice. Une abstraction n’est utile que lorsqu’elle représente une véritable frontière de responsabilité ou de variation.
Patterns, UML et outils de conception
En 1994, Design Patterns: Elements of Reusable Object-Oriented Software popularise des modèles comme Factory, Observer, Strategy, Decorator ou Adapter. Leur intérêt principal est de fournir un vocabulaire commun pour des structures récurrentes, pas d’imposer des recettes.
À la même époque, UML unifie plusieurs méthodes de modélisation. Ses diagrammes de classes deviennent emblématiques de la conception objet. Aujourd’hui, son usage est plus variable, mais les schémas restent utiles pour communiquer sur des architectures complexes.
Patterns et diagrammes sont des outils de raisonnement : ni l’un ni l’autre ne remplace une conception adaptée au problème réel.
Tests, dépendances et limites des abstractions
L’encapsulation, les interfaces et l’injection de dépendances peuvent améliorer la testabilité en permettant de remplacer certaines dépendances par des fakes, stubs ou mocks.
Mais fragmenter une architecture uniquement pour faciliter le mocking peut produire l’effet inverse. Les pratiques modernes équilibrent tests unitaires, intégration et vérification du comportement réel.
Le même principe vaut pour l’architecture : plus d’abstractions ne signifie pas automatiquement meilleure conception.
Les critiques de la programmation orientée objet
À mesure que la POO devient dominante, ses limites apparaissent plus clairement : hiérarchies profondes fragiles, état mutable partagé, objets fortement interconnectés, abstractions prématurées et architectures « enterprise » accumulant couches et factories.
Ces critiques ne rendent pas l’objet obsolète. Elles montrent qu’un paradigme largement adopté finit par être appliqué à des problèmes pour lesquels il n’est pas toujours le meilleur choix.
L’objet et les autres paradigmes
La programmation fonctionnelle remet notamment en avant fonctions pures et données immuables. Java ajoute lambdas et streams, C# LINQ, C++ des lambdas toujours plus riches, tandis que Python et JavaScript traitent depuis longtemps les fonctions comme des valeurs.
L’opposition « objet contre fonctionnel » est donc moins pertinente aujourd’hui : les langages modernes combinent plusieurs approches.
De même, un domaine complexe peut bénéficier d’objets riches qui protègent des invariants, tandis qu’un pipeline de transformation de données sera parfois plus clair avec des structures simples et des fonctions composables.
Interfaces graphiques, jeux et systèmes distribués
Les interfaces graphiques et les jeux restent des domaines où état, identité et comportement se prêtent naturellement aux objets. Pourtant, les moteurs de jeux utilisent aussi des modèles Entity Component System (ECS) privilégiant composition et séparation des données plutôt que de profondes hiérarchies.
Dans les systèmes distribués, le modèle acteur organise le calcul autour d’entités qui reçoivent des messages et maintiennent leur propre état. Un acteur n’est pas un objet Java ou C++, mais cette approche rappelle l’insistance d’Alan Kay sur les messages et l’isolation.
Qu’est-ce qui reste réellement de la POO ?
Après plusieurs décennies, certaines idées paraissent particulièrement durables : encapsulation, polymorphisme, composition, identité et état lorsque le domaine les exige.
En revanche, l’idée qu’une bonne architecture doit nécessairement former une grande hiérarchie de classes a largement perdu de son influence. La POO moderne est souvent plus sobre que celle enseignée dans les années 1990.
Smalltalk, C++, Java, Python et JavaScript sont suffisamment différents pour montrer que la POO est mieux comprise comme une famille de modèles de programmation que comme une checklist absolue.
Pourquoi apprendre encore la programmation orientée objet ?
Une immense quantité de logiciels et de frameworks repose encore sur classes et objets. Les concepts d’encapsulation, responsabilité, interface et polymorphisme dépassent d’ailleurs les langages strictement orientés objet.
Comprendre la POO permet aussi d’en reconnaître les limites. La maturité consiste moins à « faire de l’objet partout » qu’à choisir la bonne représentation pour le problème.
À retenir
La programmation orientée objet naît progressivement dans les années 1960 avec Simula, conçu par Ole-Johan Dahl et Kristen Nygaard pour représenter les acteurs de simulations.
Dans les années 1970, Smalltalk et Alan Kay développent une vision centrée sur des objets autonomes qui communiquent par messages. C++ diffuse ensuite classes, héritage et polymorphisme dans la programmation industrielle sans devenir exclusivement orienté objet.
Dans les années 1990, Java contribue à faire de la POO un modèle dominant, tandis que Python et JavaScript montrent que les objets peuvent coexister avec des modèles plus dynamiques ou multiparadigmes.
L’expérience a corrigé plusieurs excès : l’héritage n’est plus la réponse naturelle à toute réutilisation, et composition, interfaces, immuabilité et fonctions occupent une place croissante.
La leçon la plus durable n’est probablement pas « tout doit être une classe », mais plutôt : regrouper les responsabilités cohérentes, cacher les détails inutiles et permettre aux composants de collaborer sans dépendre excessivement de leur implémentation interne.
Questions fréquentes
Qu’est-ce que la programmation orientée objet ?
C’est une famille d’approches qui organise le logiciel autour d’objets possédant généralement un état, une identité et des comportements, et qui collaborent à travers des méthodes, messages ou interfaces.
Qui a inventé la programmation orientée objet ?
Il n’existe pas un inventeur unique. Ole-Johan Dahl et Kristen Nygaard ont introduit dans Simula plusieurs mécanismes fondateurs. Alan Kay et l’équipe Smalltalk ont ensuite développé et popularisé une autre vision centrée sur les messages.
Quelle est la différence entre une classe et un objet ?
Une classe décrit généralement une structure et des comportements communs. Un objet est une instance particulière possédant son propre état. Les systèmes à prototypes n’utilisent cependant pas ce modèle de la même manière.
Quels sont les principaux principes de la POO ?
On cite souvent encapsulation, abstraction, héritage et polymorphisme. Dans la pratique moderne, composition, interfaces et maîtrise des dépendances sont tout aussi importantes.
L’héritage est-il obligatoire ?
Non. De nombreux systèmes orientés objet utilisent peu d’héritage et privilégient la composition ou les interfaces.
JavaScript est-il orienté objet ?
Oui, mais son modèle historique repose sur les prototypes plutôt que sur les classes classiques. La syntaxe class moderne s’appuie toujours sur ce mécanisme.
Python est-il orienté objet ?
Python prend largement en charge l’orientation objet, mais il est multiparadigme et n’oblige pas à organiser tout le code en classes.
La programmation orientée objet est-elle dépassée ?
Non. Elle reste très utilisée, mais elle n’est plus considérée comme la solution universelle à tous les problèmes.
Faut-il préférer la composition à l’héritage ?
Souvent, la composition produit des composants plus souples et moins couplés. L’héritage reste pertinent lorsqu’une véritable relation de spécialisation existe et que le contrat de la classe de base est stable.
Pourquoi la POO reste-t-elle importante ?
Parce que ses idées d’encapsulation, de responsabilités, de contrats et de polymorphisme restent utiles pour maîtriser la complexité, même dans des architectures multiparadigmes.