Pourquoi avons-nous besoin d'une classe immuable?


Je ne peux pas obtenir quels sont les scénarios où nous avons besoin d'une classe immuable.
Avez-vous déjà fait face à une telle exigence? ou pouvez - vous nous donner un exemple réel où nous devrions utiliser ce modèle.

Author: Rakesh Juyal, 2010-09-22

19 answers

Les autres réponses semblent se concentrer sur l'explication des raisons pour lesquelles l'immutabilité est bonne. Il est très bon et je l'utilise autant que possible. Cependant, ce n'est pas votre question. Je vais prendre votre question point par point pour essayer de vous assurer que vous obtenez les réponses et les exemples dont vous avez besoin.

Je ne peux pas obtenir quels sont les scénarios où nous avons besoin d'une classe immuable.

"Besoin" est un terme relatif ici. Les classes immuables sont un modèle de conception qui, comme tout paradigme / modèle / outil, est là pour faciliter la construction de logiciels. De même, beaucoup de code a été écrit avant l'arrivée du paradigme OO, mais comptez-moi parmi les programmeurs qui "ont besoin" OO. Les classes immuables, comme OO, ne sont pas strictement nécessaires, mais je vais agir comme si j'en avais besoin.

Avez - vous déjà fait face à une telle exigence?

Si vous ne regardez pas les objets du domaine à problème avec la bonne perspective, vous ne verrez peut-être pas condition pour un objet immuable. Il peut être facile de penser qu'un domaine problématique ne nécessite pas de classes immuables si vous ne savez pas quand les utiliser avantageusement.

J'utilise souvent des classes immuables où je pense à un objet donné dans mon domaine problématique comme une valeur ou une instance fixe. Cette notion dépend parfois de la perspective ou du point de vue, mais idéalement, il sera facile de basculer dans la bonne perspective pour identifier le bon candidat objet.

Vous pouvez avoir une meilleure idée de l'endroit où les objets immuables sont vraiment utiles (si ce n'est strictement nécessaire) en vous assurant de lire divers livres/articles en ligne pour développer un bon sens de la façon de penser aux classes immuables. Un bon article pour vous aider à démarrer est Java theory and practice: muter ou ne pas muter?

Je vais essayer de donner quelques exemples ci-dessous de la façon dont on peut voir des objets dans différentes perspectives (mutable vs immuable) à clarifiez ce que je veux dire par perspective.

... pouvez-vous nous donner un exemple réel où nous devrions utiliser ce modèle.

Puisque vous avez demandé des exemples réels, je vais vous en donner, mais commençons d'abord par quelques exemples classiques.

Objets de valeur classiques

Chaînes et entiers et sont souvent considérés comme des valeurs. Par conséquent, il n'est pas surprenant de constater que la classe String et la classe Integer wrapper (ainsi que les autres classes wrapper) sont immuable en Java. Une couleur est généralement considérée comme une valeur, donc la classe de couleur immuable.

Contre-exemple

En revanche, une voiture n'est généralement pas considérée comme un objet de valeur. La modélisation d'une voiture signifie généralement une création d'une classe qui a changer d'état (odomètre, vitesse, niveau de carburant, etc). Cependant, il existe certains domaines où il peut s'agir d'un objet de valeur. Par exemple, une voiture (ou plus précisément un modèle de voiture) peut être considéré comme un objet de valeur dans une application pour regarder les huile moteur appropriée pour un véhicule donné.

Cartes À Jouer

Avez-vous déjà écrit un programme de cartes à jouer? Je l'ai fait. J'aurais pu représenter une carte à jouer comme un objet mutable avec une couleur et un rang mutables. Une main de tirage au sort pourrait être 5 instances fixes où remplacer la 5ème carte dans ma main signifierait muter la 5ème instance de carte à jouer en une nouvelle carte en changeant sa couleur et son rang ivars.

Cependant, j'ai tendance à penser à une carte à jouer comme un objet immuable qui a un correction d'un costume et d'un rang immuables une fois créés. Ma main de poker draw serait de 5 instances et remplacer une carte dans ma main impliquerait de jeter une de ces instances et d'ajouter une nouvelle instance aléatoire à ma main.

Projection De La Carte

Un dernier exemple est quand j'ai travaillé sur un code de carte où la carte pourrait s'afficher dans diverses projections . Le code original avait la carte utiliser une instance de projection fixe, mais mutable (comme la carte à jouer mutable ci-dessus). Changer la projection de la carte signifiait muter les ivars de l'instance de projection de la carte (type de projection, point central, zoom, etc.).

Cependant, je sentais que la conception était plus simple si je pensais à une projection comme une valeur immuable ou une instance fixe. Changer la projection de la carte signifiait que la carte référencait une instance de projection différente plutôt que de muter l'instance de projection fixe de la carte. Cela a également simplifié la capture de projections nommées telles que MERCATOR_WORLD_VIEW.

 65
Author: Bert F, 2013-05-06 13:27:18

Les classes immuables sont en général beaucoup plus simples à concevoir, à implémenter et à utiliser correctement . Un exemple est String: l'implémentation de java.lang.String est significativement plus simple que celle de std::string en C++, principalement en raison de son immutabilité.

Un domaine particulier où l'immuabilité fait une différence particulièrement importante est la concurrence: les objets immuables peuvent être partagés en toute sécurité entre plusieurs threads , alors que les objets mutables doivent être sécurisés par un design et une mise en œuvre soigneux - habituellement, cela est loin d'être une tâche triviale.

Mise à Jour: Efficace Java 2nd Edition aborde cette question en détail - voir Item 15: Minimiser la mutabilité.

Voir aussi ces articles connexes:

 39
Author: Péter Török, 2017-05-23 10:31:09

Effective Java de Joshua Bloch décrit plusieurs raisons d'écrire des classes immuables:

  • Simplicité-chaque classe est dans un seul état
  • Thread Safe-parce que l'état ne peut pas être modifié, aucune synchronisation n'est requise
  • Écrire dans un style immuable peut conduire à un code plus robuste. Imaginez si les chaînes n'étaient pas immuables; Toute méthode getter renvoyant une chaîne nécessiterait que l'implémentation crée une copie défensive avant que la chaîne ne soit renvoyée-sinon un client peut accidentellement ou malicieusement briser cet état de l'objet.

En général, il est bon de rendre un objet immuable à moins qu'il n'y ait de graves problèmes de performance en conséquence. Dans de telles circonstances, les objets mutables builder peuvent être utilisés pour construire des objets immuables, par exemple StringBuilder

 36
Author: Tarski, 2014-07-18 18:08:05

Hashmaps sont un exemple classique. Il est impératif que la clé d'une carte soit immuable. Si la clé n'est pas immuable et que vous modifiez une valeur sur la clé de telle sorte que hashCode() entraînerait une nouvelle valeur, la carte est maintenant cassée (une clé est maintenant au mauvais emplacement dans la table de hachage.).

 11
Author: Kirk Woll, 2010-09-22 13:21:15

Java est pratiquement une et toutes les références. Parfois, une instance est référencée plusieurs fois. Si vous modifiez une telle instance, elle sera reflétée dans toutes ses références. Parfois, vous ne voulez tout simplement pas avoir cela pour améliorer la robustesse et la sécurité des fils. Ensuite, une classe immuable est utile pour que l'on soit obligé de créer une instance new et de la réaffecter à la référence actuelle. De cette façon, l'instance d'origine des autres références reste intacte.

Imaginez comment Java ressemblerait à si String était mutable.

 7
Author: BalusC, 2010-09-22 13:28:48

Nous n'avons pasbesoin de classes immuables, en soi, mais elles peuvent certainement faciliter certaines tâches de programmation, en particulier lorsque plusieurs threads sont impliqués. Vous n'avez pas besoin d'effectuer de verrouillage pour accéder à un objet immuable, et tous les faits que vous avez déjà établis à propos d'un tel objet continueront d'être vrais à l'avenir.

 6
Author: Damien_The_Unbeliever, 2010-09-22 13:22:15

Il existe diverses raisons d'immutabilité:

  • Sécurité des threads: Les objets immuables ne peuvent pas être modifiés ni son état interne changer, il n'est donc pas nécessaire de le synchroniser.
  • Il garantit également que tout ce que j'envoie via (via un réseau) doit être dans le même état que précédemment envoyé. Cela signifie que personne (écoute) ne peut venir ajouter des données aléatoires dans mon ensemble immuable.
  • C'est aussi plus simple à développer. Vous garantissez qu'aucune sous-classe n'existera si un l'objet est immuable. Par exemple, une classe String.

Donc, si vous voulez envoyer des données via un service réseau, et que vous voulez un sentiment de garantie que vous aurez votre résultat exactement le même que ce que vous avez envoyé, définissez-le comme immuable.

 5
Author: Buhake Sindi, 2010-09-22 13:44:22

Prenons un cas extrême: les constantes entières. Si j'écris une déclaration comme "x=x + 1", je veux être 100% confiant que le nombre" 1 " ne deviendra pas en quelque sorte 2, peu importe ce qui se passe ailleurs dans le programme.

Maintenant, d'accord, les constantes entières ne sont pas une classe, mais le concept est le même. Supposons que j'écris:

String customerId=getCustomerId();
String customerName=getCustomerName(customerId);
String customerBalance=getCustomerBalance(customerid);

Semble assez simple. Mais si les chaînes n'étaient pas immuables, alors je devrais considérer la possibilité que getCustomerName puisse changer customerId, de sorte que lorsque J'appelle getCustomerBalance, j'obtiens le solde pour un client différent. Maintenant, vous pourriez dire: "Pourquoi dans le monde quelqu'un qui écrit une fonction getCustomerName le ferait-il changer l'id? Cela n'aurait aucun sens."Mais c'est exactement là que vous pourriez avoir des ennuis. La personne qui écrit le code ci-dessus peut considérer comme évident que les fonctions ne changeraient pas le paramètre. Ensuite, quelqu'un arrive qui doit modifier une autre utilisation de cette fonction pour gérer le cas où un client a plusieurs comptes sous le même nom. Et il dit: "Oh, voici cette fonction de nom getCustomer pratique qui recherche déjà le nom. Je vais juste faire que changer automatiquement l'ID au compte suivant avec le même nom, et le mettre dans une boucle ..."Et puis votre programme commence mystérieusement à ne pas fonctionner. Serait-ce un mauvais style de codage? Sûrement. Mais c'est précisément un problème dans les cas où l'effet secondaire n'est PAS évident.

Immutabilité signifie simplement qu'une certaine classe d'objets, sont constantes, et nous pouvons les traiter comme des constantes.

(Bien sûr, l'utilisateur pourrait affecter un "objet constant" différent à une variable. Quelqu'un peut écrire String s="bonjour"; et puis écrire plus tard s="au revoir"; À moins que je ne rende la variable finale, je ne peux pas être sûr qu'elle ne soit pas modifiée dans mon propre bloc de code. Tout comme les constantes entières m'assurer que "1" est toujours le même nombre, mais pas que "x=1" ne sera jamais changé par écrit "x=2". Mais je peux être confiant que si j'ai un gérer à un objet immuable, qu'aucune fonction à laquelle je le passe ne peut le changer sur moi, ou que si j'en fais deux copies, qu'une modification de la variable contenant une copie ne changera pas l'autre. Etc.

 5
Author: Jay, 2010-09-22 14:04:42

Je vais attaquer cela d'un point de vue différent. Je trouve que les objets immuables me facilitent la vie lors de la lecture de code.

Si j'ai un objet mutable, je ne suis jamais sûr de sa valeur s'il est utilisé en dehors de ma portée immédiate. Disons que je crée MyMutableObject dans les variables locales d'une méthode, le remplis avec des valeurs, puis le passe à cinq autres méthodes. N'importe laquelle de ces méthodes peut changer l'état de mon objet, donc l'une des deux choses doit se produire:

  1. je dois garder une trace des corps de cinq méthodes supplémentaires tout en pensant à la logique de mon code.
  2. Je dois faire cinq copies défensives inutiles de mon objet pour m'assurer que les bonnes valeurs sont transmises à chaque méthode.

Le premier rend le raisonnement sur mon code difficile. La seconde fait que mon code aspire les performances-je suis essentiellement en train d'imiter un objet immuable avec une sémantique de copie sur écriture de toute façon, mais en le faisant tout le temps que les méthodes appelées modifient réellement mon objet état.

Si j'utilise plutôt MyImmutableObject, je peux être assuré que ce que j'ai défini est ce que seront les valeurs pour la vie de ma méthode. Il n'y a pas d ' "action fantasmagorique à distance" qui le changera sous moi et il n'est pas nécessaire que je fasse des copies défensives de mon objet avant d'invoquer les cinq autres méthodes. Si les autres méthodes veulent changer les choses à leurs fins elles doivent faire la copie – mais elles ne le font que si elles doivent vraiment faire une copie (contrairement à ce que je fais avant chaque externe de l'appel de méthode). Je m'épargne les ressources mentales de garder une trace des méthodes qui ne sont peut-être même pas dans mon fichier source actuel, et j'épargne au système la surcharge de faire sans cesse des copies défensives inutiles au cas où.

(Si je vais en dehors du monde Java et dans, disons, le monde C++, entre autres, je peux devenir encore plus délicat. Je peux faire apparaître les objets comme s'ils étaient mutables, mais dans les coulisses, les faire cloner de manière transparente sur n'importe quel type de changement d'état-c'est copier-sur-écrire-avec personne n'étant le plus sage.)

 5
Author: JUST MY correct OPINION, 2010-09-22 15:07:42

L'utilisation du mot-clé final ne rend pas nécessairement quelque chose d'immuable:

public class Scratchpad {
    public static void main(String[] args) throws Exception {
        SomeData sd = new SomeData("foo");
        System.out.println(sd.data); //prints "foo"
        voodoo(sd, "data", "bar");
        System.out.println(sd.data); //prints "bar"
    }

    private static void voodoo(Object obj, String fieldName, Object value) throws Exception {
        Field f = SomeData.class.getDeclaredField("data");
        f.setAccessible(true);
        Field modifiers = Field.class.getDeclaredField("modifiers");
        modifiers.setAccessible(true);
        modifiers.setInt(f, f.getModifiers() & ~Modifier.FINAL);
        f.set(obj, "bar");
    }
}

class SomeData {
    final String data;
    SomeData(String data) {
        this.data = data;
    }
}

Juste un exemple pour démontrer que le mot-clé "final" est là pour empêcher l'erreur du programmeur, et pas beaucoup plus. Alors que la réaffectation d'une valeur manquant d'un mot-clé final peut facilement se produire par accident, aller à cette longueur pour changer une valeur devrait être fait intentionnellement. Il est là pour la documentation et pour éviter les erreurs du programmeur.

 1
Author: Mark Peters, 2010-09-22 15:12:49

Les structures de données immuables peuvent également aider lors du codage d'algorithmes récursifs. Par exemple, disons que vous essayez de résoudre un problème 3SAT. Une façon est de faire ce qui suit:

  • Choisissez une variable non affectée.
  • Donnez-lui la valeur de TRUE. Simplifiez l'instance en supprimant les clauses qui sont maintenant satisfaites et réapparaissez pour résoudre l'instance la plus simple.
  • Si la récursivité sur le cas TRUE a échoué, attribuez plutôt cette variable FALSE. Simplifiez cette nouvelle instance, et réapparaître pour le résoudre.

Si vous avez une structure mutable pour représenter le problème, alors lorsque vous simplifiez l'instance dans la branche TRUE, vous devrez soit:

  • Gardez une trace de toutes les modifications que vous apportez et annulez-les toutes une fois que vous réalisez que le problème ne peut pas être résolu. Cela a une surcharge importante car votre récursivité peut aller assez loin, et il est difficile de coder.
  • Faites une copie de l'instance, puis modifiez la copie. Ce sera lent car si votre récursivité est a quelques dizaines de niveaux de profondeur, vous devrez faire de nombreuses copies de l'instance.

Cependant, si vous le codez de manière intelligente, vous pouvez avoir une structure immuable, où toute opération renvoie une version mise à jour (mais toujours immuable) du problème (similaire à String.replace - elle ne remplace pas la chaîne, vous en donne juste une nouvelle). La façon naïve d'implémenter cela est d'avoir la structure" immuable " simplement copier et en faire une nouvelle sur n'importe quelle modification, en la réduisant à la 2ème solution en ayant un mutable, avec tous ces frais généraux, mais vous pouvez le faire de manière plus efficace.

 1
Author: Claudiu, 2010-09-22 15:27:12

L'une des raisons du "besoin" de classes immuables est la combinaison de tout passer par référence et de ne pas prendre en charge les vues en lecture seule d'un objet (c'est-à-dire C++'s const).

Considérons le cas simple d'une classe prenant en charge le modèle d'observateur:

class Person {
    public string getName() { ... }
    public void registerForNameChange(NameChangedObserver o) { ... }
}

Si string n'étaient pas immuables, il serait impossible pour la classe Person d'implémenter registerForNameChange() correctement, car quelqu'un pourrait écrire ce qui suit, modifiant efficacement le nom de la personne sans déclenchement de toute notification.

void foo(Person p) {
    p.getName().prepend("Mr. ");
}

En C++, getName() renvoyer un const std::string& a pour effet de renvoyer par référence et d'empêcher l'accès aux mutateurs, ce qui signifie que les classes immuables ne sont pas nécessaires dans ce contexte.

 1
Author: André Caron, 2010-09-22 15:36:08

Ils nous donnent également une garantie. La garantie d'immutabilité signifie que nous pouvons les développer et créer de nouveaux modèles d'efficacité qui ne sont pas possibles autrement.

Http://en.wikipedia.org/wiki/Singleton_pattern

 1
Author: Ryan Hayes, 2010-09-22 15:38:36

Une caractéristique des classes immuables qui n'a pas encore été appelée: stocker une référence à un objet de classe profondément immuable est un moyen efficace de stocker tout l'état qu'il contient. Supposons que j'ai un objet mutable qui utilise un objet profondément immuable pour contenir 50K d'informations d'état. Supposons, en outre, que je souhaite à 25 reprises faire une" copie "de mon objet original (mutable) (par exemple pour un tampon" annuler"); l'état pourrait changer entre les opérations de copie, mais généralement faire une "copie" de l'objet mutable nécessiterait simplement de copier une référence à son état immuable, donc 20 copies équivaudraient simplement à 20 références. En revanche, si l'état était détenu dans 50K d'objets mutables, chacune des 25 opérations de copie devrait produire sa propre copie de 50K de données; la détention des 25 copies nécessiterait de détenir plus d'une valeur meg de données principalement dupliquées. Même si la première opération de copie produirait une copie des données qui ne le sera jamais changer, et les 24 autres opérations pourraient en théorie simplement se référer à cela, dans la plupart des implémentations, il n'y aurait aucun moyen pour le deuxième objet demandant une copie de l'information de savoir qu'une copie immuable existe déjà(*).

(*) Un modèle qui peut parfois être utile est que les objets mutables aient deux champs pour contenir leur état-un sous forme mutable et un sous forme immuable. Les objets peuvent être copiés comme mutables ou immuables, et commenceraient la vie avec l'un ou l'autre ensemble de référence. Dès que l'objet souhaite changer d'état, il copie l'immuable référence à la mutable (s'il n'a pas déjà été fait) et invalide l'immuable. Lorsque l'objet est copié comme immuable, si sa référence immuable n'est pas définie, une copie immuable sera créée et la référence immuable pointée vers cela. Cette approche nécessitera quelques opérations de copie de plus qu'une "copie à part entière en écriture" (par exemple, demander de copier un objet qui a été muté puisque la dernière copie nécessiterait une opération de copie, même si l'objet d'origine n'est plus jamais muté) mais cela évite les complexités de threading que FFCOW entraînerait.

 1
Author: supercat, 2012-05-02 15:51:35

Les objets immuables sont des instances dont les états ne changent pas une fois initialisés. L'utilisation de tels objets est spécifique aux exigences.

La classe immuable est bonne pour la mise en cache et elle est sans danger pour les threads.

 1
Author: Shreya Agarwal, 2018-06-20 05:03:32

De Java efficace; Une classe immuable est simplement une classe dont les instances ne peuvent pas être modifiées. Tout les informations contenues dans chaque instance sont fournies lors de sa création et sont fixe pour la durée de vie de l'objet. Les bibliothèques de la plate-forme Java contiennent de nombreuses classes immuables, y compris String, les classes primitives en boîte et BigInte- ger et BigDécimal. Il y a beaucoup de bonnes raisons à cela: les classes immuables sont plus faciles à concevoir, à implémenter et à utiliser que les classes mutables. Ils sont moins enclin à l'erreur et sont plus sûrs.

 0
Author: tarikfasun, 2015-07-27 14:26:55

Par immutabilité, vous pouvez être sûr que le comportement ne changera pas, avec cela vous obtenez l'avantage supplémentaire d'effectuer des opérations supplémentaires:

  • Vous pouvez utiliser plusieurs core/traitement(traitement simultané) avec au facilité (car la séquence n'aura plus d'importance.)

  • Peut faire mise en cache pour une opération coûteuse(que vous êtes sûr de la même
    résultat).

  • Peut faire débogage à l'aise(comme l'histoire de la course ne sera pas préoccupation
    plus)

 0
Author: Arun Pratap Singh, 2017-05-08 17:41:25

Pourquoi une classe immuable?

Une fois qu'un objet est instancié, son état ne peut pas être modifié pendant la durée de vie. Ce qui le rend également sûr.

Exemples :

Évidemment Chaîne, Entier et BigDecimal etc. Une fois que ces valeurs sont créées, elles ne peuvent pas être modifiées pendant la durée de vie.

Cas d'utilisation : Une fois que l'objet de connexion à la base de données est créé avec ses valeurs de configuration vous n'avez peut être pas besoin de changer son état où vous pouvez utiliser un immuable classe

 0
Author: bharanitharan, 2018-01-04 06:56:01

Mes 2 cents pour les futurs visiteurs:


2 les scénarios où les objets immuables sont de bons choix sont:

En multi-threading

Les problèmes de concurrence dans un environnement multithread peuvent très bien être résolus par synchronisation, mais la synchronisation est une affaire coûteuse (ne creuserait pas ici sur "pourquoi"), donc si vous utilisez des objets immuables, il n'y a pas de synchronisation pour résoudre le problème de concurrence car l'état des objets immuables ne peut pas être modifié, et si l'état ne peut pas être changé tous les threads peuvent accéder de manière transparente à l'objet. Ainsi, les objets immuables constituent un excellent choix pour les objets partagés dans un environnement multithread.


Comme clé pour les collections basées sur le hachage

L'une des choses les plus importantes à noter lorsque vous travaillez avec une collection basée sur le hachage est que la clé doit être telle que son {[0] } doit toujours renvoyer la même valeur pour la durée de vie de l'objet, car si cette valeur est modifiée, l'ancienne entrée effectuée dans la collection basée sur le hachage, l'utilisation de cet objet ne peut pas être récupérée, ce qui provoquerait une fuite de mémoire. Puisque l'état des objets immuables ne peut pas être modifié, ils font donc un excellent choix comme clé dans la collection basée sur le hachage. Donc, si vous utilisez un objet immuable comme clé pour une collection basée sur le hachage, vous pouvez être sûr qu'il n'y aura aucune fuite de mémoire à cause de cela (bien sûr, il peut toujours y avoir une fuite de mémoire lorsque l'objet utilisé comme clé n'est référencé nulle part ailleurs, mais ce n'est pas le cas le point ici).

 0
Author: hagrawal, 2018-08-21 20:45:31