J'adore le «modèle» immuable en raison de ses points forts, et dans le passé, je l'ai trouvé bénéfique pour concevoir des systèmes avec des types de données immuables (certains, la plupart ou même tous). Souvent, lorsque je le fais, je me retrouve à écrire moins de bogues et le débogage est beaucoup plus facile.
Cependant, mes pairs en général évitent l'immuable. Ils ne sont pas du tout inexpérimentés (loin de là), mais ils écrivent des objets de données de manière classique - des membres privés avec un getter et un setter pour chaque membre. Ensuite, généralement, leurs constructeurs ne prennent aucun argument, ou peuvent simplement prendre quelques arguments pour plus de commodité. Si souvent, la création d'un objet ressemble à ceci:
Foo a = new Foo();
a.setProperty1("asdf");
a.setProperty2("bcde");
Peut-être qu'ils font ça partout. Peut-être qu'ils ne définissent même pas un constructeur qui prend ces deux chaînes, quelle que soit leur importance. Et peut-être qu'ils ne changent pas la valeur de ces chaînes plus tard et n'en ont jamais besoin. De toute évidence, si ces choses sont vraies, l'objet serait mieux conçu comme immuable, non? (le constructeur prend les deux propriétés, pas de setters du tout).
Comment décidez-vous si un type d'objet doit être conçu comme immuable?Existe-t-il un bon ensemble de critères pour le juger?
Je suis actuellement en train de débattre de la nécessité de changer certains types de données de mon propre projet en immuables, mais je devrais le justifier auprès de mes pairs, et les données dans les types pourraient (TRÈS rarement) changer - à quel moment vous pouvez bien sûr changer il de façon immuable (créez-en un nouveau, en copiant les propriétés de l'ancien objet à l'exception de celles que vous souhaitez modifier). Mais je ne sais pas si c'est juste mon amour pour les immuables qui transparaît, ou s'il y a un réel besoin / bénéfice d'eux.
la source
Réponses:
On dirait que vous vous en approchez à l'envers. Il faut par défaut immuable. Rendez un objet modifiable uniquement si vous devez absolument / ne pouvez tout simplement pas le faire fonctionner comme un objet immuable.
la source
Le principal avantage des objets immuables est de garantir la sécurité des fils. Dans un monde où plusieurs cœurs et threads sont la norme, cet avantage est devenu très important.
Mais l'utilisation d'objets mutables est très pratique. Ils fonctionnent bien et tant que vous ne les modifiez pas à partir de threads séparés et que vous avez une bonne compréhension de ce que vous faites, ils sont assez fiables.
la source
Il existe deux façons principales de décider si un objet est immuable.
a) En fonction de la nature de l'objet
Il est facile d'attraper ces situations car nous savons que ces objets ne changeront pas après leur construction. Par exemple, si vous avez une
RequestHistory
entité et par nature, les entités historiques ne changent pas une fois qu'elle est construite. Ces objets peuvent être directement conçus comme des classes immuables. Gardez à l'esprit que l'objet de requête est modifiable car il peut changer son état et à qui il est affecté, etc. au fil du temps, mais l'historique des demandes ne change pas. Par exemple, un élément d'historique a été créé la semaine dernière lorsqu'il est passé de l'état soumis à l'état affecté ET cet historique ne peut jamais changer. Il s'agit donc d'un cas immuable classique.b) En fonction du choix de conception, des facteurs externes
Ceci est similaire à l'exemple java.lang.String. Les chaînes peuvent en fait changer au fil du temps, mais par conception, elles l'ont rendu immuable en raison de facteurs de mise en cache / pool de chaînes / simultanéité. De même, la mise en cache / simultanéité, etc. peut jouer un bon rôle pour rendre un objet immuable si la mise en cache / simultanéité et les performances associées sont vitales dans l'application. Mais cette décision doit être prise très soigneusement après avoir analysé tous les impacts.
Le principal avantage des objets immuables est qu'ils ne sont pas soumis à un motif de désherbage. L'objet ne détectera aucun changement pendant la durée de vie et rend le codage et la maintenance très très faciles.
la source
Minimiser l'état d'un programme est très bénéfique.
Demandez-leur s'ils souhaitent utiliser un type de valeur mutable dans l'une de vos classes pour le stockage temporaire à partir d'une classe client.
S'ils disent oui, demandez pourquoi? L'état mutable n'appartient pas à un scénario comme celui-ci. Les forcer à créer l'état auquel il appartient réellement et à rendre l'état de vos types de données aussi explicite que possible sont d'excellentes raisons.
la source
La réponse est quelque peu dépendante de la langue. Votre code ressemble à Java, où ce problème est aussi difficile que possible. En Java, les objets ne peuvent être transmis que par référence et le clone est complètement rompu.
Il n'y a pas de réponse simple, mais pour certains, vous voulez rendre les objets de petite valeur immuables. Java a correctement rendu les chaînes immuables, mais a incorrectement modifié la date et le calendrier.
Rendez donc les objets de petite valeur immuables et implémentez un constructeur de copie. Oubliez tout sur Cloneable, il est si mal conçu qu'il est inutile.
Pour les objets de plus grande valeur, s'il est gênant de les rendre immuables, rendez-les faciles à copier.
la source
Un peu contre-intuitivement, ne jamais avoir besoin de changer les chaînes plus tard est un assez bon argument selon lequel peu importe si les objets sont immuables ou non. Le programmeur les traite déjà comme effectivement immuables, que le compilateur les applique ou non.
L'immuabilité ne fait généralement pas de mal, mais elle n'aide pas toujours non plus. Le moyen le plus simple de savoir si votre objet pourrait bénéficier de l'immuabilité est de savoir si vous avez besoin de faire une copie de l'objet ou d'acquérir un mutex avant de le modifier . Si cela ne change jamais, alors l'immuabilité ne vous achète vraiment rien et rend parfois les choses plus compliquées.
Vous faire un bon point sur le risque de construire un objet dans un état non valide, mais ce qui est vraiment une question distincte de l' immuabilité. Un objet peut être à la fois modifiable et toujours dans un état valide après sa construction.
L'exception à cette règle est que, puisque Java ne prend en charge ni les paramètres nommés ni les paramètres par défaut, il peut parfois devenir difficile de concevoir une classe qui garantit un objet valide en utilisant simplement des constructeurs surchargés. Pas tellement avec le cas des deux propriétés, mais il y a aussi quelque chose à dire pour la cohérence, si ce modèle est fréquent avec des classes similaires mais plus grandes dans d'autres parties de votre code.
la source
Object
fonctionnalités cachées ", puis changer directement un objet ne serait pas sémantiquement différent de remplacer la référence par une référence à un nouvel objet qui était identique mais pour le changement indiqué. L'une des plus grandes faiblesses sémantiques de Java, à mon humble avis, est qu'il n'a aucun moyen par lequel le code peut indiquer qu'une variable devrait seulement la seule référence non éphémère à quelque chose; les références ne doivent être transmissibles qu'aux méthodes qui ne peuvent pas conserver de copie après leur retour.Je pourrais avoir une vue trop bas de cela et probablement parce que j'utilise C et C ++ qui ne rendent pas exactement si simple de rendre tout immuable, mais je vois les types de données immuables comme un détail d'optimisation afin d'écrire plus des fonctions efficaces dépourvues d'effets secondaires et capables de fournir très facilement des fonctionnalités telles que les systèmes d'annulation et l'édition non destructive.
Par exemple, cela pourrait être extrêmement coûteux:
... if
Mesh
n'est pas conçu pour être une structure de données persistante et était, à la place, un type de données qui nécessitait de le copier en entier (ce qui pourrait s'étendre sur des gigaoctets dans certains scénarios) même si tout ce que nous allons faire est de changer un une partie de celui-ci (comme dans le scénario ci-dessus où nous ne modifions que les positions des sommets).C'est donc lorsque j'atteins l'immuabilité et que je conçois la structure de données pour permettre à des parties non modifiées d'être copiées et comptées de manière superficielle, pour permettre à la fonction ci-dessus d'être raisonnablement efficace sans avoir à copier en profondeur des maillages entiers tout en étant en mesure d'écrire le fonctionner sans effets secondaires, ce qui simplifie considérablement la sécurité des threads, la sécurité des exceptions, la possibilité d'annuler l'opération, de l'appliquer de manière non destructive, etc.
Dans mon cas, il est trop coûteux (au moins du point de vue de la productivité) de tout rendre immuable, donc je le garde pour les classes qui sont trop chères pour être copiées en profondeur. Ces classes sont généralement des structures de données lourdes comme des maillages et des images et j'utilise généralement une interface mutable pour leur exprimer des modifications via un objet "constructeur" pour obtenir une nouvelle copie immuable. Et je ne fais pas tant cela pour essayer d'obtenir des garanties immuables au niveau central de la classe que pour m'aider à utiliser la classe dans des fonctions qui peuvent être exemptes d'effets secondaires. Mon désir de rendre
Mesh
immuable ci-dessus n'est pas directement de rendre les mailles immuables, mais de permettre d'écrire facilement des fonctions exemptes d'effets secondaires qui entrent et sortent un nouveau sans payer une énorme mémoire et un coût de calcul en échange.En conséquence, je n'ai que 4 types de données immuables dans toute ma base de code, et ce sont toutes des structures de données lourdes, mais je les utilise beaucoup pour m'aider à écrire des fonctions qui sont exemptes d'effets secondaires. Cette réponse pourrait être applicable si, comme moi, vous travaillez dans une langue qui ne rend pas si facile tout rendre immuable. Dans ce cas, vous pouvez vous concentrer sur le fait que l'essentiel de vos fonctions évite les effets secondaires, auquel cas vous souhaiterez peut-être rendre certaines structures de données immuables, les types PDS, en tant que détail d'optimisation pour éviter les copies complètes coûteuses. Pendant ce temps, quand j'ai une fonction comme celle-ci:
... alors je n'ai aucune tentation de rendre les vecteurs immuables car ils sont assez bon marché pour simplement copier en entier. Il n'y a aucun avantage de performance à gagner ici en transformant les vecteurs en structures immuables qui peuvent copier en profondeur des pièces non modifiées. Ces coûts dépasseraient le coût de la simple copie du vecteur entier.
la source
Si Foo n'a que deux propriétés, il est facile d'écrire un constructeur qui accepte deux arguments. Mais supposons que vous ajoutiez une autre propriété. Ensuite, vous devez ajouter un autre constructeur:
À ce stade, c'est toujours gérable. Mais que faire s'il y a 10 propriétés? Vous ne voulez pas d'un constructeur avec 10 arguments. Vous pouvez utiliser le modèle de générateur pour construire des instances, mais cela ajoute de la complexité. À ce moment, vous vous demanderez "pourquoi n'ai-je pas simplement ajouté des setters pour toutes les propriétés comme le font les gens normaux"?
la source