Ce sera une question très non technique et douce et je ne sais pas si c'est la bonne plateforme. Mais je suis un étudiant CS débutant, donc j'espère que vous le tolérez.
Au premier semestre, nous avons été initiés à OOP concepts comme l'encapsulation, le masquage des données, la modularité, l'héritage, etc. via Java et UML. (Java est mon premier langage de programmation)
D'après ce que je comprends, OOP est un moyen de gérer la complexité des logiciels. Mais ses principes ne sont ni nouveaux ni uniques, ils sont en quelque sorte universels dans tous les domaines de l'ingénierie.
Par exemple, une voiture est une structure très complexe dont la complexité est gérée par une hiérarchie de composants modulaires et encapsulés avec des comportements et des interfaces bien définis.
Mais je ne comprends pas la raison de l'introduction d'un nouveau paradigme de programmation. Je pense que tous les principes utilisés pour gérer la complexité peuvent être réalisés par des langages de programmation procédurale. Par exemple, pour la modularité, nous pouvons simplement diviser le programme en plusieurs petits programmes qui effectuent des tâches bien définies dont le code est contenu dans des fichiers séparés. Ces programmes interagiraient les uns avec les autres grâce à leurs entrées et sorties bien définies. Les fichiers peuvent être protégés (cryptés?) Pour réaliser l'encapsulation. Pour la réutilisation du code, nous pouvons simplement appeler ces fichiers chaque fois qu'ils sont nécessaires dans de nouveaux programmes. Cela ne capture-t-il pas tout ce qui OOP est ou est-ce que je manque quelque chose de très évident?
Je ne demande pas de preuve que OOP gère la complexité. À mon avis, c'est certainement le cas. Mais je pense que tous les principes utilisés pour gérer la complexité comme la modularité, l'encapsulation, le masquage des données, etc. peuvent être très facilement implémenté par les langages procéduraux. Alors pourquoi vraiment OOP si on peut gérer la complexité sans ça?
Permettez-moi d'essayer avec une réponse théorique vraiment faible :)
Ce que vous demandez vraiment, c'est: Pourquoi inclure la prise en charge de l'Orientation d'Objet (OO) directement dans le langage lorsque des langages procéduraux peuvent être utilisés pour concevoir et écrire OO code?
Et la réponse est: Pour avoir une norme sur la façon dont OO est exprimé dans le code source afin que vous ne vous retrouviez pas avec 22 implémentations différentes pour la même abstraction.
Par exemple, supposons que je crée un MagicButton
et un MagicSlider
qui peuvent être utilisés dans un système d'interface utilisateur. J'ai besoin d'un moyen de regrouper les méthodes qui peuvent être utilisées avec le MagicButton, les méthodes qui ne peuvent être utilisées qu'avec le MagicSlider et les méthodes qui peuvent être utilisées par les deux. Ces objets partagent certaines méthodes car ils sont tous les deux des objets Magic gui.
Je peux faire le regroupement en nommant les fonctions d'une manière spéciale MagicSlider_DoSomething ...
, en incluant les méthodes dans des fichiers spécifiques nommés d'une manière spéciale MagicSliderMethods.XXX
, ou je pourrais trouver un autre moyen spécial de faire la même chose. S'il n'y a pas de méthode standard dans la langue pour le faire, je le ferai différemment de vous et différemment de n'importe qui d'autre. Cela rend le partage de code beaucoup plus difficile.
Oui, l'envoi tardif - les méthodes virtuelles dans les langues OO - peuvent être implémentées dans les langages procéduraux, mais il y a tellement de façons différentes de l'implémenter. Selon qui a écrit le code, vous vous retrouverez avec différents implémentations de OO à l'intérieur du même programme.
Pensez au mauvais développeur de maintenance. Cette personne doit gérer différentes abstractions d'objets et différentes façons d'appeler des méthodes virtuelles en fonction de l'auteur du code d'origine.
Aussi: Avoir les abstractions dans le langage permet aux éditeurs de code avancés comme Eclipse de faire beaucoup d'analyses statiques sur le code. Par exemple, Eclipse peut proposer une liste de toutes les méthodes qui peuvent être utilisées sur un objet, ainsi que l'implémentation automatique de "méthodes TODO" vides. Eclispe sait exactement quelles méthodes votre classe doit implémenter en fonction des classes que vous étendez et des interfaces que vous implémentez. Ce serait presque impossible s'il n'y avait pas de norme linguistique pour faire l'OO.
Au premier semestre, nous avons été initiés à OOP concepts comme l'encapsulation, le masquage des données, la modularité, l'héritage, etc. via Java et UML. (Java est mon premier langage de programmation)
Aucun de ces concepts n'est OOP. Ils existent tous en dehors de OO, indépendamment de OO et beaucoup ont même été inventés avant OO).
Donc, si vous pensez que ça est ce que OO est tout, alors votre conclusion est juste: vous pouvez faire tout cela dans des langages procéduraux, car ils n'ont rien à voir avec OO .
Par exemple, l'un des articles fondateurs sur la modularité est sur les critères à utiliser dans la décomposition des systèmes en modules . Il n'y est pas fait mention de OO là-dedans. (Il a été écrit en 1972, alors OO était encore une niche obscure, bien qu'il y ait déjà plus d'une décennie) vieux.)
Alors que Data Abstraction est important dans OO, il s'agit plus d'une conséquence de la caractéristique principale de OO (Messaging) que c'est une caractéristique déterminante. De plus, il est très important de se rappeler qu'il existe différents types d'abstraction des données. Les deux types d'abstraction de données les plus courants utilisés aujourd'hui (si nous ignorons "aucune abstraction que ce soit", qui est probablement encore plus utilisé que les deux autres combinés), sont Types de données abstraits et Objets. Donc, juste en disant "Masquage des informations", "Encapsulation" et "Abstraction des données", vous n'avez rien dit sur OO, car OO n'est que un sous forme d'abstraction de données, et les deux sont en fait fondamentalement différents:
En passant, cela signifie qu'en Java, les classes ne sont pas orientées objet. Deux instances de la même classe can accèdent à la représentation et à l'implémentation privée de l'autre. Par conséquent, les instances de classes ne sont pas des objets, ce sont en fait des instances ADT. Java interface
s, cependant, do fournit une abstraction orientée objet. Donc, en d'autres termes: seules les instances d'interfaces sont des objets en Java, les instances de classes ne le sont pas.
Fondamentalement, pour les types, vous ne pouvez utiliser que des interfaces. Cela signifie des types de paramètres de méthodes et de constructeurs, des types de méthodes de retour, des types de champs d'instance, des champs statiques et des champs locaux, l'argument d'un opérateur instanceof
ou d'un opérateur de cast, et des arguments de type pour un constructeur de type générique doivent toujours être des interfaces. Une classe ne peut être utilisée que directement après l'opérateur new
, nulle part ailleurs.
Par exemple, pour la modularité, nous pouvons simplement diviser le programme en plusieurs petits programmes qui effectuent des tâches bien définies dont le code est contenu dans des fichiers séparés. Ces programmes interagiraient les uns avec les autres grâce à leurs entrées et sorties bien définies. Les fichiers peuvent être protégés (cryptés?) Pour réaliser l'encapsulation. Pour la réutilisation du code, nous pouvons simplement appeler ces fichiers chaque fois qu'ils sont nécessaires dans de nouveaux programmes. Cela ne capture-t-il pas tout ce qui OOP est ou est-ce que je manque quelque chose de très évident?
Ce que vous décrivez est OO.
C'est en effet une bonne façon de penser à OO. En fait, c'est à peu près exactement ce que les inventeurs originaux de OO avaient en tête. (Alan Kay est allé plus loin: il envisageait beaucoup de petits ordinateurs qui s'envoyaient des messages sur le réseau).) Ce que vous appelez "programme" est généralement appelé "objet" et au lieu de "appeler", nous disons généralement "envoyer un message".
L'orientation des objets concerne tout Messagerie (aka répartition dynamique). Le terme "orienté objet" a été inventé par le Dr Alan Kay, le concepteur principal de Smalltalk, et il le définit comme ceci :
Pour moi, la POO signifie uniquement la messagerie, la conservation et la protection locales et la dissimulation du processus étatique, et la liaison tardive extrême de toutes choses.
Décomposons cela:
En ce qui concerne l'implémentation, la messagerie est un appel de procédure à liaison tardive, et si les appels de procédure sont à liaison tardive, vous ne pouvez pas savoir au moment de la conception quoi vous allez appeler, vous ne pouvez donc pas en faire hypothèses sur la représentation concrète de l’État. Donc, il s'agit vraiment de messagerie, la liaison tardive est une implémentation de la messagerie et l'encapsulation en est la conséquence.
Il a précisé plus tard que " La grande idée est la" messagerie " ", et regrette de l'avoir appelé "orienté objet" au lieu de "orienté message", car le terme "orienté objet" met la se concentrer sur la chose sans importance (objets) et détourne l'attention de ce qui est vraiment important (messagerie):
Juste un petit rappel que j'ai pris la peine lors du dernier OOPSLA pour essayer de rappeler à tout le monde que Smalltalk n'est pas seulement PAS sa syntaxe ou la bibliothèque de classes, il ne s'agit même pas de classes. Je suis désolé d'avoir inventé il y a longtemps le terme "objets" pour ce sujet, car cela amène beaucoup de gens à se concentrer sur l'idée moindre.
La grande idée est la "messagerie" - c'est de cela que parle le noyau de Smalltalk/Squeak (et c'est quelque chose qui n'a jamais été complètement terminé dans notre phase Xerox PARC). Les Japonais ont un petit mot - ma - pour "ce qui est entre" - peut-être que l'équivalent anglais le plus proche est "interstitiel". La clé pour créer des systèmes grands et évolutifs est beaucoup plus de concevoir la façon dont ses modules communiquent plutôt que ce que devraient être leurs propriétés et comportements internes. Pensez à Internet - pour vivre, il (a) doit permettre de nombreux types d'idées et de réalisations qui dépassent toute norme unique et (b) permettre divers degrés d'interopérabilité sûre entre ces idées.
(Bien sûr, aujourd'hui, la plupart des gens ne se concentrent même pas sur les objets mais sur les classes, ce qui est encore plus faux.)
La messagerie est fondamentale vers OO, à la fois comme métaphore et comme mécanisme.
Si vous envoyez un message à quelqu'un, vous ne savez pas ce qu'il en fait. La chose seulement que vous pouvez observer est leur réponse. Vous ne savez pas s'ils ont traité le message eux-mêmes (c'est-à-dire si l'objet a une méthode), s'ils ont transmis le message à quelqu'un d'autre (délégation/mandataire), s'ils l'ont même compris. C'est à cela que sert l'encapsulation, c'est à cela que sert OO. Vous ne pouvez même pas distinguer un proxy de la réalité, tant qu'il répond à la façon dont vous vous y attendez.
Un terme plus "moderne" pour "messagerie" est "envoi de méthode dynamique" ou "appel de méthode virtuelle", mais qui perd la métaphore et se concentre sur le mécanisme.
Donc, il y a deux façons de regarder la définition d'Alan Kay: si vous la regardez seule, vous pouvez observer que la messagerie est essentiellement un appel de procédure à liaison tardive et que la liaison tardive implique l'encapsulation, nous pouvons donc conclure que # 1 et # 2 sont en fait redondants, et OO est une question de liaison tardive.
Cependant, il a précisé plus tard que l'important est la messagerie, et que nous pouvons donc voir les choses sous un angle différent: la messagerie est liée tardivement. Maintenant, si la messagerie était la chose seulement possible, alors # 3 serait trivialement vrai: s'il n'y a qu'une seule chose, et que cette chose est liée tardivement, alors tout les choses sont retardées. Et encore une fois, l'encapsulation découle de la messagerie.
Des points similaires sont également présentés dans Sur la compréhension de l'abstraction des données, revisité par William R. Cook et aussi son Proposition pour des définitions simplifiées et modernes de "objet" et "orienté objet" :
La répartition dynamique des opérations est la caractéristique essentielle des objets. Cela signifie que l'opération à invoquer est une propriété dynamique de l'objet lui-même. Les opérations ne peuvent pas être identifiées statiquement, et il n'y a généralement aucun moyen de [savoir] exactement quelle opération sera exécutée en réponse à une demande donnée, sauf en l'exécutant. C'est exactement la même chose qu'avec les fonctions de première classe, qui sont toujours distribuées dynamiquement.
Dans Smalltalk-72, il n'y avait même pas d'objets! Il y avait seulement flux de messages qui ont été analysés, réécrits et redirigés. Les premières méthodes sont arrivées (méthodes standard pour analyser et rediriger les flux de messages), puis les objets (regroupements de méthodes qui partagent un certain état privé). L'héritage est venu beaucoup plus tard, et les classes n'ont été introduites que comme moyen de prendre en charge l'héritage. Si le groupe de recherche de Kay connaissait déjà les prototypes, ils n'auraient probablement jamais introduit de cours en premier lieu.
Benjamin Pierce dans Types et langages de programmation fait valoir que la caractéristique qui définit l'Object-Orientation est Open Recursion .
Donc: selon Alan Kay, OO est tout au sujet de la messagerie. Selon William Cook, OO est tout au sujet de la répartition de méthode dynamique (ce qui est vraiment la même chose Selon Benjamin Pierce, OO est tout à propos de la récursivité ouverte, ce qui signifie essentiellement que les auto-références sont résolues dynamiquement (ou du moins c'est une façon de penser), ou, en d'autres termes , Messagerie.
Comme vous pouvez le voir, la personne qui a inventé le terme "OO" a une vision plutôt métaphysique des objets, Cook a une vue plutôt pragmatique et Pierce une vue mathématique très rigoureuse. Mais l'important est: le philosophe, le pragmatiste et le théoricien sont tous d'accord! La messagerie est le seul pilier d'OO. Période.
Notez qu'il n'y a aucune mention d'héritage ici! L'héritage n'est pas essentiel pour l'OO. En général, la plupart des langages OO ont un moyen de réutilisation de l'implémentation, mais cela ne doit pas nécessairement être un héritage. Il peut également s'agir d'une forme de délégation, par exemple. En fait, - Le traité d'Orlando discute délégation comme alternative à l'héritage et comment différentes formes de délégation et d'héritage conduisent à différents points de conception dans le espace de conception de langages orientés objet. (Notez qu'en fait, même dans les langages qui prennent en charge l'héritage, comme Java, les gens apprennent à l'éviter, ce qui indique à nouveau que ce n'est pas nécessaire pour l'OO.)
Mais je pense que tous les principes utilisés pour gérer la complexité comme la modularité, l'encapsulation, le masquage des données, etc. peuvent être très facilement mis en œuvre par les langages procéduraux.
Lorsque vous dites "très facilement", vous faites une déclaration très audacieuse. La façon dont je le lis est: "Je ne vois pas la difficulté, donc elle ne doit pas être très grande." Formulé de cette façon, il devient clair que vous ne demandez pas "pourquoi avons-nous besoin d'OO", vous demandez "pourquoi les difficultés rencontrées par d'autres paradigmes de programmation qui ont conduit à l'invention de OO immédiatement évident pour moi? "
Une réponse à cette question est que bon nombre de ces difficultés n'existent pas dans les types de programmes sur lesquels vous travaillez. On ne vous demande pas de mettre à jour le code de spaghetti vieux de 40 ans. Vous n'essayez pas d'écrire un nouveau gestionnaire d'affichage pour un système d'exploitation. Vous ne déboguez pas des applications distribuées multithread.
Pour de nombreux types de programmes de jouets que nous, étudiants CS, sommes chargés d'écrire, nous pourrions tout aussi bien les écrire en BASIC ou en Assemblage que Java ou Python. C'est parce que la complexité inhérente de la les tâches sont si faibles, il n'y a qu'un seul développeur, il n'y a pas de problèmes d'interopérabilité hérités, les performances n'ont pas d'importance et le code ne sera probablement exécuté qu'une poignée de fois sur une seule machine.
Imaginez prendre un élève conducteur et lui demander de fusionner dans une rue animée aux heures de pointe, sur une transmission manuelle sans synchromesh, en montant une colline escarpée. Catastrophe. Pourquoi? Ils sont incapables de gérer le niveau de complexité requis pour suivre simultanément toutes les règles requises par la tâche.
Imaginez maintenant le même élève, le même véhicule, qui roule à pied dans un parking vide. Ils sont OK, car leur niveau de compétence est adéquat pour la tâche. Il n'y a pas de pression, peu de risques et ils peuvent prendre les sous-tâches individuelles de démarrage, d'embrayage, de changement de vitesse, d'accélération, de direction une à la fois.
Cet étudiant pourrait se demander pourquoi nous avons des transmissions automatiques, si un conducteur qualifié peut faire toutes ces choses simultanément? La réponse est qu'un conducteur suffisamment qualifié n'a pas, dans des conditions optimales, besoin d'une automatique. Mais nous ne sommes pas tous des conducteurs professionnels dans des conditions optimales, et nous souhaitons généralement que les concepteurs de la voiture s'occupent de toute cette complexité pour nous.
Un programmeur compétent et suffisamment discipliné peut en effet créer un système fonctionnel de haute complexité en C, ou Assembly. Mais nous ne sommes pas tous Linus Torvalds. Nous ne devons pas non plus l'être, pour créer des logiciels utiles.
Personnellement, je n'ai aucun intérêt à devoir réinventer toutes les fonctionnalités d'une langue moderne avant même de pouvoir résoudre le problème. Si je peux profiter d'un langage qui comprend des solutions à des problèmes déjà résolus, pourquoi ne le ferais-je pas?
Je vais donc tourner votre question et vous demander, si les langues offrent des fonctionnalités pratiques telles que l'encapsulation et le polymorphisme, pourquoi ne devrions-nous pas les utiliser?
Ce que vous décrivez n'est pas de la POO, c'est de l'abstraction. L'abstraction est présente dans tous les modèles de design modernes, même ceux qui ne sont pas OOP. Et OOP est un type d'abstraction très spécifique.
Tout d'abord, il convient de noter qu'il n'existe pas de définition unique de la POO, il peut donc y avoir des personnes en désaccord avec ce que je qualifie de POO.
Deuxièmement, il est important de se rappeler que OOP a été inspiré par les modèles de conception traditionnels, donc les similitudes avec la conception de voitures ne sont pas une coïncidence.
Cependant, voici quelques façons OOP est plus nuancé que ce que vous avez dit:
Encapsulation: il ne s'agit pas seulement d'avoir une interface définie pour un module (c'est-à-dire l'abstraction), il s'agit d'interdire l'accès au-delà de cette interface. En Java, l'accès à une variable privée est une erreur de compilation, tandis que dans la conception de votre voiture, vous pouvez (dans certains cas) utiliser les choses d'une manière différente de l'interface prévue.
Héritage: C'est vraiment la chose qui rend OOP unique. Une fois que vous avez défini une interface, vous pouvez faire plusieurs choses implémentant cette interface, et vous pouvez le faire de manière héréditaire, en modifiant des parties spécifiques de leur implémentation, tout en héritant toutes les parties précédentes, réduisant considérablement la duplication de code.
Si vous pensez en termes de composants encapsulés d'une voiture, il n'y a pas vraiment d'équivalent à cela. Il n'y a aucun moyen pour moi de fabriquer un équipement en prenant un équipement différent et en modifiant une partie spécifique de sa mise en œuvre. (Au moins, je ne pense pas, je ne connais pas grand-chose aux voitures).
Polymorphisme : Une fois que vous avez défini une interface, tout ce qui utilise cette interface doit être indiscernable, du point de vue des opérations disponibles, et vous ne devriez pas 'ai pas besoin de savoir quelle implémentation est utilisée pour utiliser une interface. C'est là que le sous-typage et le Liskov Substitution Principle deviennent importants.
Couplage : Un aspect clé de OOP est que nous relions étroitement les choses avec les mêmes opérations et répartissons les différentes qu'ils peuvent avoir. Les données sont regroupées avec des opérations sur ces données. Cela signifie qu'il est très facile d'ajouter une nouvelle forme de données (une nouvelle implémentation), mais très difficile d'ajouter une nouvelle opération à une interface (puisque vous ' Je dois mettre à jour chaque classe qui implémente l'interface) .Ceci contraste avec les types de données algébriques dans les langages fonctionnels, où il est très facile d'ajouter une nouvelle opération (vous écrivez simplement une fonction qui traite tous les cas), mais difficile à ajouter une nouvelle variante (puisque vous devez ajouter un nouveau cas à toutes vos fonctions).
Avons-nous vraiment besoin de OO langages pour gérer la complexité du logiciel?
Cela dépend de la signification de la Parole, "besoin".
Si "besoin" signifie exige, non, nous ne l'exigeons pas.
Si "besoin" signifie "offre de solides avantages", alors je dirais "oui", nous le désirons.
Les langages OO lient la fonctionnalité aux données.
Vous pouvez éviter cette liaison et écrire des fonctions qui transmettent les valeurs des données.
Mais vous vous retrouverez probablement avec des constellations de données qui vont de pair et vous commencerez à faire circuler des tuples, des enregistrements ou des dictionnaires de données.
Et vraiment, c'est tout ce que les appels de méthode sont: des fonctions partielles sur des ensembles de données liés.
Caractéristiques de la POO:
Cependant, rien de tout cela ne se produit aussi facilement qu'avec un langage orienté objet avec un support de première classe de ces fonctionnalités.
Il y a beaucoup de critiques de la POO .
Cependant, les études semblent indiquer que la réutilisation du code via la POO nous permet d'augmenter la productivité du programmeur. C'est une conclusion controversée, et certains chercheurs disent qu'ils ne peuvent pas reproduire ces gains de productivité, compte tenu de certaines contraintes. (source)
Nous n'avons pas "besoin" de POO. Mais dans certains cas, l'utilisateur veut POO.
Ma compréhension est que les programmeurs matures peuvent être assez productifs dans le style orienté objet. Et lorsque les packages ont des objets de base avec des interfaces simples et faciles à comprendre, même les nouveaux programmeurs peuvent rapidement devenir très productifs.
J'essaierai d'être bref.
Le principe de base de OO est la combinaison de données et de comportement dans une seule unité organisationnelle (un objet).
C'est ce qui nous permet de contrôler la complexité et c'était un concept assez innovant quand il est apparu. Comparez cela aux fichiers d'une part (données pures), aux programmes qui lisent et traitent ces fichiers d'autre part (logique pure) et sortez (données pures à nouveau).
Ce n'est qu'une fois que vous avez rassemblé ce paquet de données et de logique, en modélisant une entité du monde réel, que vous pouvez commencer à échanger des messages, créer des classes enfants, séparer les données et le comportement privés des données et du comportement publics, implémenter un comportement polymorphe, faire toute cette magie spécifique aux OO.
Donc, oui, OO est un gros problème. Et non, ce n'est pas seulement un tas de vieux trucs avec un nom de fantaisie.
Tout démonter, regarder les éléments et dire ensuite "oh, eh bien, il n'y a rien ici que je n'ai jamais vu auparavant", ce n'est pas reconnaître l'Assemblée qui détient l'innovation. Le résultat est plus que la somme de ses parties.
Il n'y a pas de définition "officielle" de la programmation orientée objet, et des gens raisonnables ne s'entendent pas sur ce qui définit réellement la qualité de l'OO. Certains disent que la messagerie, certains disent le sous-typage, certains disent l'héritage, certains disent le regroupement des données et du comportement. Cela ne signifie pas que le terme n'a pas de sens, juste que vous ne devriez pas vous laisser trop prendre à se chamailler sur ce que réel OO est.
L'encapsulation et la modularité sont des principes de conception plus fondamentaux et devraient être appliqués dans tous les paradigmes de programmation. Les partisans de OO ne prétendent pas que ces propriétés ne peuvent être obtenues qu'avec OO - seulement que OO est particulièrement bien adapté) Bien entendu, les partisans d'autres paradigmes, comme par exemple la programmation fonctionnelle, revendiquent la même chose pour leur paradigme. Dans la pratique, de nombreux langages réussis sont multi-paradigmes et l'OO, fonctionnel, etc. devrait être considéré comme un outil plutôt que comme "une seule vraie voie" ".
Je pense que tous les principes utilisés pour gérer la complexité peuvent être réalisés par des langages de programmation procédurale.
C'est vrai, car au final, vous pouvez faire n'importe quoi dans n'importe quel langage de programmation. Cela pourrait être plus facile dans certaines langues que dans d'autres, car toutes les langues ont des forces et des faiblesses différentes.
Quelque chose que les autres réponses n'ont pas mentionné: l'état.
Vous parlez de OO comme un outil pour gérer complexité. Qu'est-ce que la complexité? C'est un terme flou. Nous avons tous ce sens gestalt de ce que cela signifie, mais il est plus difficile de Nous pourrions mesurer la complexité cyclomatique, c'est-à-dire le nombre de chemins d'exécution à travers le code, mais je ne sais pas de quoi nous parlons lorsque nous utilisons OO pour gérer complexité.
Ce que je pense dont nous parlons est la complexité liée à l'état.
Il y a deux idées principales derrière encapsulation. L'un d'eux, la dissimulation de détails d'implémentation, est assez bien couvert dans les autres réponses. Mais un autre cache son état d'exécution. Nous ne fouinons pas avec les données internes des objets; nous transmettons des messages (ou appelons des méthodes si vous préférez les détails d'implémentation au concept comme l'a souligné Jörg Mittag). Pourquoi?
Les gens ont déjà mentionné que c'est parce que vous ne pouvez pas changer la structure interne de vos données sans changer le code qui y accède, et vous voulez le faire en un seul endroit (la méthode d'accesseur) au lieu de 300 endroits.
Mais c'est aussi parce qu'il rend le code difficile à raisonner: le code procédural (que ce soit dans un langage de nature procédurale ou simplement écrit dans ce style) offre peu d'aide pour imposer des restrictions sur la mutation de Etat. Tout peut changer à tout moment et de n'importe où. L'appel de fonctions/méthodes peut avoir une action effrayante à distance. Les tests automatisés sont plus difficiles, car le succès des tests est déterminé par la valeur des variables non locales qui sont largement accessibles/accessibles.
Les deux autres grands paradigmes de programmation (OO et fonctionnel) offrent des solutions intéressantes mais presque diamétralement opposées au problème de la complexité liée à l'état. En programmation fonctionnelle, on essaie de l'éviter complètement: les fonctions sont généralement pures, les opérations sur les structures de données retournent des copies plutôt que de mettre à jour l'original en place, etc.
OO d'autre part propose des outils pour gérer l'état de gestion (au lieu d'outils pour l'éviter). En plus des outils au niveau de la langue comme les modificateurs d'accès (protégés/publics/privés), les getters et les setters, etc., il existe également un certain nombre de conventions connexes comme la loi de Demeter qui déconseille d'accéder aux objets pour accéder à d'autres données d'objets. .
Notez que vous n'avez pas besoin d'objets pour faire vraiment tout cela: vous pourriez avoir une fermeture qui a des données inaccessibles et retourne une structure de données de fonctions pour la manipuler. Mais n'est-ce pas un objet? Cela ne correspond-il pas à notre conception de ce qu'est un objet, intuitivement? Et si nous avons ce concept, ne vaut-il pas mieux le reformuler dans le langage plutôt que (comme d'autres réponses l'ont dit) de s'appuyer sur une explosion combinatoire d'implémentations ad hoc concurrentes?
Avons-nous vraiment besoin de OO langues pour gérer la complexité des logiciels?
Non. Mais ils peuvent aider dans de nombreuses situations.
J'ai utilisé principalement un seul langage OO pendant des décennies, mais la plupart de mon code est en fait strictement procédural de style pré-OO. Cependant, pour tout ce qui implique une interface graphique, j'utilise la vaste bibliothèque OO du langage de méthodes et d'objets intégrés, car elle simplifie énormément mon code.
Par exemple, une application Windows utilisant l'API Windows de bas niveau d'origine pour afficher un formulaire, un bouton et un champ d'édition nécessite beaucoup de code, tandis que les bibliothèques d'objets fournies avec Visual Basic ou C # ou Delphi en font de même programme minuscule et trivial. Ainsi, mon code OO est généralement relativement petit et pour l'interface graphique, tandis que mon code que ces objets invoquent est généralement beaucoup plus grand et ne se soucie généralement pas d'être OO (bien qu'il puisse varier selon sur le problème que j'essaie de résoudre).
J'ai vu OO programmes qui étaient trop compliqués, s'appuyaient sur des règles ésotériques compliquées sur la façon dont les objets étaient implémentés, et auraient pu être beaucoup plus simples s'ils avaient été écrits sans OO concepts. J'ai également vu le contraire: des systèmes complexes demandent à être réimplémentés et simplifiés en utilisant des objets.
Au fur et à mesure que vous acquérez de l'expérience, vous constaterez que différentes situations nécessitent différents outils et solutions, et une taille unique ne convient pas à tous.
En tant que personne impliquée dans un très grand projet entièrement écrit en C, je peux certainement dire que la réponse est clairement "non".
La modularité est importante. Mais la modularité peut être implémentée dans pratiquement n'importe quel langage décent. Par exemple, C prend en charge la compilation modulaire, les fichiers d'en-tête et les types de structure. C'est suffisant pour 99% des cas. Définissez un module pour chaque nouveau type de données abstrait dont vous avez besoin et définissez les fonctions pour opérer sur le type de données. Parfois, vous voulez des performances et ces fonctions sont dans le fichier d'en-tête en tant que fonctions en ligne, d'autres fois, vous utiliserez des fonctions standard. Tout est invisible pour l'utilisateur dans le sens choisi.
Les structures soutiennent la composition. Par exemple, vous pouvez avoir une table de hachage verrouillée composée d'un verrou mutex et d'une table de hachage standard. Ce n'est pas une programmation orientée objet; aucun sous-classement n'est effectué. La composition est un outil beaucoup plus ancien que l'idée de programmation orientée objet.
Pour le 1% des cas où la modularité au niveau de la compilation n'est pas suffisante et où vous avez besoin d'une modularité d'exécution, il existe une chose appelée pointeurs de fonction. Ils permettent d'avoir des implémentations individuelles d'une interface bien définie. Notez qu'il ne s'agit pas d'une programmation orientée objet dans un langage non orienté objet. Il s'agit de définir une interface, puis de l'implémenter. Par exemple, le sous-classement n'est pas utilisé ici.
Considérez peut-être le projet open source le plus complexe qui soit. À savoir, le noyau Linux. Il est entièrement écrit en langage C. Cela se fait principalement en utilisant les outils de modularité de niveau de compilation standard, y compris la composition, puis de temps en temps chaque fois que la modularité d'exécution est nécessaire, des pointeurs de fonction sont utilisés pour définir et implémenter une interface.
Si vous essayez de trouver un exemple de programmation orientée objet dans le noyau Linux, je suis sûr que trouver un tel exemple est très difficile, sauf si vous étendez la programmation orientée objet pour inclure des tâches standard telles que "définir une interface puis l'implémenter".
Notez que même le langage de programmation C prend en charge la programmation orientée objet si vous en avez vraiment besoin. Par exemple, considérons la boîte à outils de l'interface utilisateur graphique GTK. Il est en fait orienté objet, bien qu'écrit dans un langage non orienté objet. Cela montre donc que l'idée que vous avez besoin d'un "langage orienté objet" est profondément erronée. Il n'y a rien qu'un langage orienté objet puisse faire qu'un autre type de langage ne puisse pas faire. De plus, si vous êtes un programmeur expert, vous savez très facilement écrire du code orienté objet dans n'importe quel langage. Ce n'est pas un fardeau d'utiliser C, par exemple.
Ainsi, les conclusions sont que les langages orientés objet ne sont probablement utiles que pour les programmeurs débutants qui ne comprennent pas comment le concept est réellement implémenté. Cependant, je ne voudrais pas être près d'un projet où les programmeurs sont des programmeurs novices.
La raison de l'introduction de paradigmes de programmation, y compris des méthodes orientées objet, est de faciliter la création de programmes plus sophistiqués et plus puissants. Dans le numéro d'août 1981 de Byte Magazine, Daniel Ingalls , l'un des principaux créateurs de Smalltalk, a défini "orienté objet" comme impliquant les capacités suivantes:
Tels étaient les principes identifiés par Ingalls comme étant les éléments moteurs de la conception du Smalltalk-80 développé par Xerox Parc Research. Dans l'article du magazine susmentionné, vous pouvez lire une description détaillée de chacun de ces principes et comment ils contribuent au paradigme orienté objet selon Ingalls.
Tous ces principes peuvent être appliqués en utilisant n'importe quel langage complet de Turing, qu'il s'agisse de langage procédural, d'assemblage ou autre. Ce sont des principes de conception, pas une spécification de langage. Un langage orienté objet est destiné à faciliter l'utilisation de ces principes lors de la création de logiciels.
Par exemple, pour prendre le premier des principes d'Ingall (gestion automatique du stockage), n'importe qui peut écrire son propre système de gestion automatique du stockage dans un langage procédural, mais ce serait beaucoup de travail de le faire. Lorsque vous utilisez un langage tel que Smalltalk ou Java qui intègre la gestion automatique du stockage, le programmeur n'a pas à faire autant de travail de gestion de la mémoire. Le compromis est que le programmeur obtient moins de contrôle sur le l'utilisation de la mémoire. Il y a donc un avantage et un inconvénient. L'idée d'un paradigme de conception comme la programmation orientée objet est que les avantages du paradigme l'emporteront sur les inconvénients pour au moins certains programmeurs.
C'est une très bonne question et je pense que les réponses données ici n'ont pas rendu justice, alors je vais continuer et ajouter mes réflexions.
L'objectif est - Gérer la complexité logicielle . Le but n'est pas "d'utiliser OO langue").
Il n'y a pas de "raison" derrière l'introduction d'un nouveau paradigme. C'est quelque chose qui s'est produit naturellement lorsque le codage est devenu plus mature. Il est plus logique d'écrire du code où nous ajoutons un entraîneur à la fin du train (le train étant modélisé à l'aide d'une liste chaînée) plutôt que d'ajouter un nouveau nœud à la fin de la liste chaînée.
Le codage en termes d'entités du monde réel est tout simplement la façon évidente et correcte de coder lorsque nous codons sur les entités du monde réel.
Un ordinateur peut travailler avec l'ajout d'un nœud à la fin de la liste chaînée aussi facilement qu'avec l'ajout d'un coach supplémentaire à la fin du train. Mais pour les humains, il est plus facile de travailler avec le train et l'entraîneur qu'avec la liste et les nœuds liés, même si lorsque nous allons plus loin, nous trouvons le train modélisé au moyen d'une liste liée.
La protection ou le cryptage des fichiers ne peut pas réaliser l'encapsulation. Le contraire du cryptage est le décryptage. L'opposé de l'encapsulation est décapsulation ce qui signifie Décomposition des structures et des classes dans les langages de programmation pour obtenir de meilleures performances. Les performances obtenues en réduisant le trafic mémoire et en évitant OOP contrôle des règles.
Par conséquent, vous pouvez écrire du code à la fois chiffré et bien encapsulé car ces deux concepts sont différents.
L'encapsulation permet de gérer la complexité du fait qu'elle est proche de la réalité.
Par conséquent, programmez dans les objets car, il est plus facile pour vous de coder et il est plus rapide pour vous et tout le monde de comprendre.
La seule chose à retenir est la suivante:
la POO ne concerne pas les fonctionnalités du langage; elle concerne la façon dont vous structurez votre code.
La POO est une façon de penser et de concevoir l'architecture de votre code, et cela peut être fait dans à peu près n'importe quel langage. Cela inclut particulièrement ces langages de bas niveau, non OO, qui sont appelés assembleur et C. Vous pouvez faire une programmation parfaitement orientée objet dans l'assembleur, et le noyau Linux, qui est écrit en C, est assez orienté objet à bien des égards .
Cela dit, les fonctionnalités OO dans un langage réduisent considérablement la quantité de code standard que vous devez écrire pour obtenir les résultats souhaités. Lorsque vous devez définir explicitement une table de fonction virtuelle et la remplir avec les pointeurs de fonction appropriés en C, vous ne faites rien rien en Java, et vous avez terminé . OO les langues suppriment simplement tout ce qui permet la cruauté du code source, le cachant derrière Nice, des abstractions au niveau de la langue (comme les classes, les méthodes, les membres, les classes de base, les appels implicites de constructeur/destructeur, etc.) ).
Donc, non, nous n'avons pas besoin OO langues pour faire la POO. C'est juste que OOP est tellement plus facile à faire avec une langue décente OO.
Une façon de gérer la complexité du logiciel consiste à séparer le framework des actions souhaitées en utilisant entièrement un Domain Specific Language. Cela signifie que le niveau de code de programmation est distinct du niveau auquel les résultats souhaités sont configurés - un langage ou un système complètement différent. Lorsque cela est fait correctement, le code conventionnel devient essentiellement une bibliothèque, et l'utilisateur ou une autre personne créant les résultats souhaités connecte les choses avec un langage de script ou un outil de conception visuelle, tel qu'un générateur de rapports.
Pour fonctionner, cela nécessite de tracer une frontière stricte autour des opérations qui seront possibles et de la façon dont elles sont liées (langage de script ou conception visuelle, comme un outil de construction de formulaire). Les métadonnées sont un moyen important d'extraire la configuration d'exécution des détails de codage, permettant à un système de prendre en charge un large éventail de résultats souhaités. Si les limites sont définies et respectées (n'acceptant pas toutes les demandes d'extension qui viennent), vous pouvez avoir un système durable et robuste qui fonctionne pour les gens, sans qu'ils aient besoin d'être programmeurs pour accomplir ce qu'ils veulent.
Martin Fowler a écrit un livre à ce sujet, et la technique est presque aussi ancienne que la programmation elle-même. On pourrait presque dire que tous les langages de programmation sont des langages spécifiques au domaine, et donc l'idée est endémique, négligée parce qu'elle est si évidente. Mais vous pouvez toujours créer vos propres outils de script ou de conception visuelle pour vous faciliter la vie. Parfois, la généralisation d'un problème le rend beaucoup plus facile à résoudre!