Je vois les avantages de rendre les objets de mon programme immuables. Quand je pense vraiment à un bon design pour mon application, j'arrive souvent naturellement à ce que beaucoup de mes objets soient immuables. Cela arrive souvent au point où j'aimerais que tous mes objets soient immuables.
Cette question traite de la même idée, mais aucune réponse ne suggère ce qu'est une bonne approche de l'immuabilité et quand l'utiliser réellement. Existe-t-il de bons modèles de conception immuables? L'idée générale semble être de "rendre les objets immuables sauf si vous en avez absolument besoin pour changer", ce qui est inutile dans la pratique.
D'après mon expérience, l'immuabilité conduit de plus en plus mon code au paradigme fonctionnel et cette progression se produit toujours:
- Je commence à avoir besoin de structures de données persistantes (au sens fonctionnel) comme des listes, des cartes, etc.
- Il est extrêmement gênant de travailler avec des références croisées (par exemple, un nœud d'arbre référençant ses enfants pendant que les enfants référencent leurs parents), ce qui me fait ne pas utiliser du tout de références croisées, ce qui rend encore une fois mes structures de données et mon code plus fonctionnels.
- L'héritage s'arrête pour avoir un sens et je commence à utiliser la composition à la place.
- Toutes les idées de base de la POO comme l'encapsulation commencent à s'effondrer et mes objets commencent à ressembler à des fonctions.
À ce stade, je n'utilise pratiquement plus rien du paradigme OOP et je peux simplement passer à un langage purement fonctionnel. Ainsi ma question: existe-t-il une approche cohérente pour une bonne conception OOP immuable ou est-il toujours le cas que lorsque vous prenez l'idée immuable à son plein potentiel, vous finissez toujours par programmer dans un langage fonctionnel qui n'a plus besoin de rien du monde OOP? Existe-t-il de bonnes directives pour décider quelles classes doivent être immuables et lesquelles doivent rester modifiables pour s'assurer que la POO ne se désagrège pas?
Pour plus de commodité, je vais vous donner un exemple. Ayons un ChessBoard
comme une collection immuable de pièces d'échecs immuables (extension de la classe abstraitePiece
). Du point de vue POO, une pièce est responsable de générer des mouvements valides à partir de sa position sur le plateau. Mais pour générer les mouvements, la pièce a besoin d'une référence à sa planche tandis que la planche doit avoir une référence à ses pièces. Eh bien, il existe quelques astuces pour créer ces références croisées immuables en fonction de votre langage OOP mais elles sont difficiles à gérer, mieux vaut ne pas avoir de pièce pour référencer sa carte. Mais alors la pièce ne peut pas générer ses mouvements car elle ne connaît pas l'état du plateau. Ensuite, la pièce devient simplement une structure de données contenant le type de pièce et sa position. Vous pouvez ensuite utiliser une fonction polymorphe pour générer des mouvements pour toutes sortes de pièces. Ceci est parfaitement réalisable dans la programmation fonctionnelle mais presque impossible dans la POO sans vérifications de type à l'exécution et autres mauvaises pratiques de POO ... Ensuite,