Dans la plupart des langages POO, les objets sont généralement modifiables avec un nombre limité d'exceptions (comme par exemple les n-uplets et les chaînes en python). Dans la plupart des langages fonctionnels, les données sont immuables.
Les objets mutables et immuables apportent toute une liste d'avantages et d'inconvénients.
Il y a des langues qui essayent de marier les deux concepts, par exemple scala où vous avez (explicitement déclaré) des données mutables et immuables (corrigez-moi si je me trompe, ma connaissance de scala est plus que limitée).
Ma question est: Est -ce que complet immuabilité (de sic!) -Ie aucun objet ne peut muter une fois qu'il a été créé- aucun sens dans un contexte de POO?
Existe-t-il des conceptions ou des implémentations d'un tel modèle?
Fondamentalement, l'immuabilité (complète) et la POO sont-elles opposées ou orthogonales?
Motivation: En POO vous fonctionner normalement sur les données, le changement (muter) les informations sous - jacentes, en gardant les références entre ces objets. Par exemple, un objet de classe Person
avec un membre father
référençant un autre Person
objet. Si vous modifiez le nom du père, cela est immédiatement visible pour l'objet enfant sans besoin de mise à jour. Étant immuable, vous auriez besoin de construire de nouveaux objets pour le père et l'enfant. Mais vous auriez beaucoup moins de problèmes avec les objets partagés, le multi-threading, GIL, etc.
la source
Réponses:
La POO et l'immuabilité sont presque complètement orthogonales les unes aux autres. Cependant, la programmation impérative et l'immuabilité ne le sont pas.
La POO peut être résumée par deux caractéristiques principales:
Encapsulation : je n’accéderai pas directement au contenu des objets, mais je communiquerais plutôt via une interface spécifique («méthodes») avec cet objet. Cette interface peut me cacher des données internes. Techniquement, cela concerne spécifiquement la programmation modulaire plutôt que la programmation orientée objet. L'accès aux données via une interface définie est à peu près équivalent à un type de données abstrait.
Dispatch dynamique : lorsque j'appelle une méthode sur un objet, la méthode exécutée sera résolue au moment de l'exécution. (Par exemple, dans la POO basée sur une classe, je pourrais appeler une
size
méthode sur uneIList
instance, mais l'appel pourrait être résolu en une implémentation dans uneLinkedList
classe). La répartition dynamique est un moyen d'autoriser le comportement polymorphe.L'encapsulation a moins de sens sans mutabilité (il n'y a pas d' état interne qui pourrait être corrompu par une ingérence externe), mais elle tend toujours à faciliter les abstractions même lorsque tout est immuable.
Un programme impératif est constitué d'instructions exécutées séquentiellement. Une déclaration a des effets secondaires, tels que la modification de l’état du programme. Avec l'immutabilité, l'état ne peut pas être changé (bien sûr, un nouvel état pourrait être créé). Par conséquent, la programmation impérative est fondamentalement incompatible avec l’immutabilité.
Il se trouve maintenant que la programmation orientée objet a toujours toujours été associée à la programmation impérative (Simula est basée sur Algol) et que tous les langages orientés programmation traditionnels ont des racines impératives (C ++, Java, C #,… sont tous ancrés en C). Cela ne signifie pas que la POO elle-même serait impérative ou modifiable, cela signifie simplement que la mise en œuvre de la POO par ces langages permet la mutabilité.
la source
Notez qu'il existe une culture parmi les programmeurs orientés objet dans laquelle les gens présument que si vous exécutez la POO, la plupart de vos objets seront mutables, mais il s'agit d'un problème distinct de celui de savoir si la POO nécessite une mutabilité. En outre, cette culture semble évoluer lentement vers une plus grande immuabilité, en raison de l'exposition des gens à la programmation fonctionnelle.
Scala est une très bonne illustration que la mutabilité n'est pas requise pour l'orientation objet. Bien que Scala prenne en charge la mutabilité, son utilisation est déconseillée. Scala idiomatique est très orienté objet et presque entièrement immuable. Cela permet surtout la mutabilité pour la compatibilité avec Java et parce que, dans certaines circonstances, les objets immuables sont inefficaces ou compliqués à utiliser.
Comparez une liste Scala et une liste Java , par exemple. La liste immuable de Scala contient toutes les mêmes méthodes objet que la liste mutable de Java. Plus, en fait, parce que Java utilise des fonctions statiques pour des opérations comme le tri , et Scala ajoute des méthodes de style fonctionnel comme
map
. Toutes les caractéristiques de la programmation orientée objet (encapsulation, héritage et polymorphisme) sont disponibles sous une forme familière pour les programmeurs orientés objet et utilisées de manière appropriée.La seule différence que vous verrez est que lorsque vous modifiez la liste, vous obtenez un nouvel objet. Cela vous oblige souvent à utiliser des modèles de conception différents de ceux utilisés avec des objets mutables, mais cela ne vous oblige pas à abandonner complètement la POO.
la source
L’immuabilité peut être simulée dans un langage POO, en exposant uniquement les points d’accès aux objets sous forme de méthodes ou de propriétés en lecture seule qui ne modifient pas les données. L'immuabilité fonctionne de la même manière dans les langages POO que dans n'importe quel langage fonctionnel, sauf que certaines fonctionnalités de langage fonctionnel peuvent vous manquer.
Votre présomption semble être que la mutabilité est une caractéristique essentielle de l'orientation objet. Mais la mutabilité est simplement une propriété d'objets ou de valeurs. L'orientation objet englobe un certain nombre de concepts intrinsèques (encapsulation, polymorphisme, héritage, etc.) qui n'ont que peu ou rien à voir avec la mutation, et vous bénéficierez toujours des avantages de ces caractéristiques, même si vous rendiez tout immuable.
Tous les langages fonctionnels ne nécessitent pas non plus l'immuabilité. Clojure a une annotation spécifique qui permet aux types d'être mutables, et la plupart des langages fonctionnels "pratiques" disposent d'un moyen de spécifier des types mutables.
Une meilleure question à poser pourrait être "L'immuabilité complète a-t-elle un sens dans la programmation impérative ?" Je dirais que la réponse évidente à cette question est non. Pour obtenir une immuabilité complète dans la programmation impérative, vous devez renoncer à des choses comme les
for
boucles (puisque vous devez muter une variable de boucle) au profit de la récursivité, et vous programmez maintenant essentiellement de manière fonctionnelle.la source
Il est souvent utile de catégoriser les objets comme encapsulant des valeurs ou des entités, la distinction étant que, si une chose est une valeur, le code qui en fait référence ne devrait jamais voir son état changer d'une manière que le code lui-même n'a pas initiée. En revanche, un code qui contient une référence à une entité peut s’attendre à ce qu’il change de façon totalement indépendante de la volonté du titulaire de la référence.
Bien qu'il soit possible d'utiliser une valeur d'encapsulation à l'aide d'objets de types mutables ou immuables, un objet ne peut se comporter comme une valeur que si au moins l'une des conditions suivantes est remplie:
Aucune référence à l'objet ne sera jamais exposée à quoi que ce soit qui pourrait changer l'état qui y est encapsulé.
Le titulaire d'au moins une des références à l'objet connaît toutes les utilisations auxquelles une référence existante peut être affectée.
Étant donné que toutes les instances de types immuables satisfont automatiquement la première exigence, leur utilisation en tant que valeurs est simple. S'assurer que l'une ou l'autre exigence est remplie lors de l'utilisation de types mutables est, en revanche, beaucoup plus difficile. Alors que les références aux types immuables peuvent être librement échangées pour encapsuler l’état qui y est encapsulé, la transmission d’état stocké dans des types mutables nécessite soit de construire des objets enveloppants immuables, soit de copier l’état encapsulé par des objets privés dans d’autres objets qui sont: soit fourni par, soit construit pour le destinataire des données.
Les types immuables fonctionnent très bien pour transmettre des valeurs et sont souvent au moins quelque peu utilisables pour les manipuler. Cependant, ils ne sont pas très doués pour la gestion des entités. La chose la plus proche que l'on puisse avoir avec une entité dans un système avec des types purement immuables est une fonction qui, étant donné l'état du système, signalera que les attributs d'une partie de celle-ci, ou produira une nouvelle instance d'état du système qui ressemble à une fourni un, sauf pour une partie particulière de celui-ci qui sera différent d’une manière sélectionnable. De plus, si le but d’une entité est d’interfacer un code avec quelque chose qui existe dans le monde réel, il peut être impossible pour l’entité d’éviter d’exposer un état mutable.
Par exemple, si on reçoit des données via une connexion TCP, on peut produire un nouvel objet "Etat du monde" qui inclut ces données dans sa mémoire tampon sans affecter les références à l'ancien "état du monde", mais d'anciennes copies de l'état du monde qui n'inclut pas le dernier lot de données sera défectueux et ne devra pas être utilisé car il ne correspondra plus à l'état du socket TCP du monde réel.
la source
En c #, certains types sont immuables, comme string.
Cela semble en outre suggérer que le choix a été fortement pris en compte.
Bien sûr, il est très exigeant en performances d'utiliser des types immuables si vous devez modifier ce type des centaines de milliers de fois. C'est la raison pour laquelle il est suggéré d'utiliser la
StringBuilder
classe à la place de lastring
classe dans ce cas.J'ai fait une expérience avec un profileur et utiliser le type immuable est vraiment plus exigeant en ressources de processeur et de RAM.
C'est aussi intuitif si vous considérez que pour modifier une seule lettre sur une chaîne de 4 000 caractères, vous devez copier chaque caractère dans une autre zone de la RAM.
la source
string
concaténation répétée . Pour pratiquement tous les types de données / cas d'utilisation, une structure persistante efficace peut être (souvent déjà) inventée. La plupart d’entre eux ont des performances à peu près égales, même si les facteurs constants sont parfois pires.string
(représentation traditionnelle). Une "chaîne" (dans la représentation dont je parle) après 1000 modifications serait comme une chaîne fraîchement créée (contenu modulo); aucune structure de données persistante utile ou largement utilisée ne dégrade la qualité après X opérations. La fragmentation de la mémoire n'est pas un problème sérieux (vous auriez beaucoup d'allocations, oui, mais la fragmentation n'est pas un problème dans les éboueurs modernes)La totale immuabilité de tout n'a pas beaucoup de sens en POO, ou dans la plupart des autres paradigmes d'ailleurs, pour une très grande raison:
Chaque programme utile a des effets secondaires.
Un programme qui ne fait rien changer, ne vaut rien. Vous pouvez aussi ne pas l'avoir même exécuté, car l'effet sera identique.
Même si vous pensez que vous ne changez rien et que vous additionnez simplement une liste de chiffres que vous avez reçus, considérez que vous devez faire quelque chose avec le résultat - que vous l’imprimiez sur une sortie standard, l’écriviez dans un fichier, ou n'importe où. Et cela implique la mutation d'un tampon et la modification de l'état du système.
Il peut être judicieux de limiter la mutabilité aux parties qui doivent pouvoir être modifiées. Mais si absolument rien ne doit changer, alors vous ne faites rien qui vaille la peine d'être fait.
la source
Je pense que cela dépend si votre définition de la programmation orientée objet est qu’elle utilise un style de transmission de messages.
Les fonctions pures ne doivent rien muter car elles renvoient des valeurs que vous pouvez stocker dans de nouvelles variables.
Avec le style de transmission de message, vous indiquez à un objet de stocker de nouvelles données au lieu de lui demander quelles nouvelles données vous devez stocker dans une nouvelle variable.
Il est possible d'avoir des objets et de ne pas les muter, en faisant de ses méthodes des fonctions pures qui vivent à l'intérieur de l'objet plutôt qu'à l'extérieur.
Mais il n'est pas possible de mélanger le style de transmission de messages et des objets immuables.
la source