Article de reference

Objet immuable

En programmation orientée objet (POO) et fonctionnelle , un objet immuable (ou objet non modifiable ) est un objet dont l'état ne peut être modifié après sa création . Ceci cont...

orientée objet (POO) et fonctionnelle , un objet immuable (ou objet non modifiable ) est un objet dont l'état ne peut être modifié après sa création . Ceci contraste avec un objet mutable (ou objet modifiable), qui peut être modifié après sa création . Dans certains cas, un objet est considéré comme immuable même si certains de ses attributs internes changent, mais son état reste inchangé d'un point de vue externe. Par exemple, un objet qui utilise sûrs pour les threads . Parmi leurs autres avantages, on peut citer leur simplicité de compréhension et de raisonnement, ainsi que leur sécurité accrue par rapport aux objets mutables.

programmation impérative , les valeurs stockées dans les variables d'un programme et dont le contenu ne change jamais sont appelées constantes , afin de les distinguer des variables susceptibles d'être modifiées lors de l'exécution. Par exemple, les facteurs de conversion de mètres en pieds ou la valeur de pi arrondie à plusieurs décimales.

Les champs en lecture seule peuvent être calculés lors de l'exécution du programme (contrairement aux constantes, qui sont connues à l'avance), mais ne changent jamais après leur initialisation.

immuabilité faible vs forte

On parle parfois d'immuabilité de certains champs d'un objet. Cela signifie qu'il est impossible de modifier ces parties de l'état de l'objet, même si d'autres parties peuvent être modifiées ( immuabilité faible ). Si tous les champs sont immuables, l'objet est immuable. Si l'objet entier ne peut être étendu par une autre classe, il est dit fortement immuable . Ceci peut, par exemple, permettre d'imposer explicitement certaines invariances concernant la constance de certaines données de l'objet tout au long de sa durée de vie. Dans certains langages, cela se fait à l'aide d'un mot-clé (par exemple consten C++ , finalen Java ) qui désigne le champ comme immuable. D'autres langages fonctionnent à l'inverse : en OCaml , les champs d'un objet ou d'un enregistrement sont immuables par défaut et doivent être explicitement marqués mutablecomme tels.

Références à des objets

Dans la plupart des langages orientés objet , les objets peuvent être référencés par des références . Java , C++ , C# , VB.NET et de nombreux langages de script , tels que Perl , Python et Ruby , en sont des exemples . Dans ce cas, il est important de savoir si l'état d'un objet peut varier lorsque des objets sont partagés par référence.

Référencer ou copier des objets

S'il est établi qu'un objet est immuable, il est préférable d'en créer une référence plutôt que de le copier intégralement. Ceci permet d'économiser de la mémoire en évitant la duplication des données et les appels aux constructeurs et destructeurs ; cela peut également améliorer la vitesse d'exécution.

La technique de copie par référence est beaucoup plus difficile à mettre en œuvre pour les objets mutables, car toute modification apportée à une référence par un utilisateur est répercutée sur tous les autres utilisateurs. Si ce comportement n'est pas souhaité, il peut s'avérer complexe de les informer et d'obtenir leur réaction appropriée. Dans ce cas, la copie défensive de l'objet entier plutôt que de la référence constitue généralement une solution simple, mais coûteuse. Le modèle observateur offre une alternative pour gérer les modifications apportées aux objets mutables.

Copie sur écriture

La technique de copie à l'écriture (COW) combine les avantages des objets mutables et immuables et est prise en charge directement par la quasi-totalité du matériel moderne. Grâce à cette technique, lorsqu'un utilisateur demande au système de copier un objet, celui-ci crée simplement une nouvelle référence pointant toujours vers le même objet. Dès qu'un utilisateur tente de modifier l'objet via cette référence, le système en effectue une copie réelle, y applique la modification et met à jour la référence pour qu'elle pointe vers la nouvelle copie. Les autres utilisateurs ne sont pas affectés, car ils continuent de faire référence à l'objet original. Ainsi, avec la COW, tous les utilisateurs semblent disposer d'une version mutable de leurs objets, même si, lorsqu'ils ne les modifient pas, les avantages des objets immuables en termes d'économie d'espace et de rapidité sont préservés. La copie à l'écriture est courante dans les systèmes de mémoire virtuelle car elle permet d'économiser de l'espace mémoire tout en assurant une gestion correcte des actions des applications.

Stage

L'utilisation systématique de références plutôt que de copies d'objets identiques est appelée intérim . Si l'intérim est utilisé, deux objets sont considérés comme égaux si et seulement si leurs références, généralement représentées par des pointeurs ou des entiers, sont identiques. Certains langages implémentent cela automatiquement : par exemple, Python intérime automatiquement les chaînes de caractères courtes . Si l'algorithme d'intérim garantit cette intérim dans tous les cas possibles, la comparaison d'objets se réduit à la comparaison de leurs pointeurs, ce qui représente un gain de vitesse considérable dans la plupart des applications. (Même si l'algorithme n'est pas exhaustif, il existe toujours la possibilité d'une optimisation rapide lorsque les objets sont égaux et utilisent la même référence.) L'intérim est généralement utile uniquement pour les objets immuables.

Sécurité du fil

Les objets immuables sont utiles dans les applications multithread. Plusieurs threads peuvent manipuler des données représentées par des objets immuables sans craindre que ces données ne soient modifiées par d'autres threads. Les objets immuables sont donc considérés comme plus sûrs pour les threads que les objets mutables.

Violation de l'immuabilité

L'immuabilité n'implique pas que l'objet stocké en mémoire soit inaltérable. Il s'agit plutôt d'une construction de compilation indiquant ce qu'un programmeur peut faire via l'interface normale de l'objet, et non pas nécessairement ce qu'il peut absolument faire (par exemple, en contournant le système de types ou en violant la cohérence des constantes en C ou C++ ).

Détails spécifiques à la langue

En Python , Java et dans le .NET Framework , les chaînes de caractères sont des objets immuables. Java et le .NET Framework proposent des versions mutables de chaînes de caractères. En Java , il s'agit StringBufferde `string` et StringBuilder`string` (versions mutables de Java Python 3 propose une variante mutable de chaînes de caractères (octets), nommée `string` bytearray.

De plus, toutes les classes d'encapsulation primitives en Java sont immuables.

Des modèles similaires sont l' interface immuable et de programmation purement fonctionnels, il n'est pas possible de créer des objets mutables sans étendre le langage (par exemple via une bibliothèque de références mutables ou une interface de fonction étrangère ), donc tous les objets sont immuables.

Ada

En Ada , tout objet est déclaré soit variable (c'est-à-dire mutable ; généralement la valeur par défaut implicite), soit constant(c'est-à-dire immuable) via le constantmot-clé.

C#, on peut imposer l'immuabilité des champs d'une classe avec l' readonlyinstruction. En imposant l'immuabilité de tous les champs, le type devient un type immuable.

`const-correct`ShoppingCart permettrait à l'utilisateur de créer des instances de la classe et de les utiliser ensuite comme const`immuables` ou `mutables`, selon ses besoins, en fournissant deux versions différentes de la items()méthode `set`. (Notez qu'en C++, il n'est pas nécessaire, et même impossible, de fournir un constructeur spécialisé pour constles instances.)

variable membreconst depuis l'intérieur d'une méthode.

D , il existe deux qualificateurs de type , ` constint` et immutable`mutable`, pour les variables qui ne peuvent pas être modifiées. Contrairement à `int` en C++ const, `mutable` en Java finalet `mutable` en C# readonly, ils sont transitifs et s'appliquent récursivement à tout élément accessible par référence à une telle variable. La différence entre `int` constet immutable`mutable` réside dans leur objet : `int` constest une propriété de la variable ; il peut exister légalement des références mutables à la valeur référencée, c'est-à-dire que la valeur peut effectivement changer. En revanche, `mutable` immutableest une propriété de la valeur référencée : la valeur et tout élément transitivement accessible à partir d'elle ne peuvent pas être modifiés (sous peine de rompre le système de types et d'entraîner un comportement indéfini ). Toute référence à cette valeur doit être marquée `int` constou ` immutablemutable`. En résumé, pour tout type non qualifié T, ` const(T)int` est l'union disjointe de T`int` (mutable) et `mutable` immutable(T).

déclaration ou par un constructeur .

Comme constles paramètres ne conservent pas la trace de leur valeur mutable, une construction similaire, inout`is`, sert en quelque sorte de variable pour l'information de mutabilité. Une fonction de type ` const(S) function(const(T))is` renvoie des valeurs typées pour les arguments mutables, constants et immuables. En revanche, une fonction de type ` is` renvoie `true` pour les arguments mutables, ` false` pour les valeurs `const` et `false` pour les valeurs ` const(S)immuable`.inout(S) function(inout(T))STconst(S)const(T)immutable(S)immutable(T)

La conversion de valeurs immuables en valeurs mutables entraîne un comportement indéfini lors de toute modification, même si la valeur d'origine provient d'une source mutable. La conversion de valeurs mutables en valeurs immuables est autorisée s'il ne subsiste aucune référence mutable. « Une expression peut être convertie de mutable (...) à immuable si elle est unique et si toutes les expressions auxquelles elle fait référence transitivement sont soit uniques, soit immuables. » Si le compilateur ne peut pas prouver l'unicité, la conversion peut être effectuée explicitement et il incombe au programmeur de s'assurer de l'absence de références mutables.

Ce type stringest un alias pour `string` immutable(char)[], c'est-à-dire une tranche de mémoire typée de caractères immuables. La création de sous-chaînes est peu coûteuse, car elle se limite à copier et modifier un pointeur et un champ de longueur, et sûre, car les données sous-jacentes ne peuvent être modifiées. Les objets de ce type const(char)[]peuvent faire référence à des chaînes de caractères, mais aussi à des tampons mutables.

Effectuer une copie superficielle d'une constante ou d'une valeur immuable supprime la couche d'immuabilité externe : la copie d'une chaîne immuable immutable(char[])renvoie une chaîne immutable(char)[]. Le pointeur immuable et la longueur sont copiés, et les copies sont mutables. Les données référencées n'ont pas été copiées et conservent leur qualificateur, dans l'exemple immutable. Ce qualificateur peut être supprimé en effectuant une copie plus profonde, par exemple à l'aide de la dupfonction `delete`.

Java

Un exemple classique d'objet immuable est une instance de la Stringclasse Java

; s . toLowerCase (); // Ceci ne fait rien !

Cette méthode toLowerCase()ne modifie pas les données « ABC » qu'elle scontient. Elle crée un nouvel objet String auquel elle attribue les données « abc » lors de sa construction. La méthode renvoie une référence à cet objet String toLowerCase(). Pour que la chaîne scontienne les données « abc », une autre approche est nécessaire :

article détaillé ) est utilisé pour implémenter des types primitifs immuables et des références d'objets , mais il ne peut pas, à lui seul, rendre les objets eux-mêmes immuables. Voir les exemples ci-dessous :

Les variables de type primitif ( int, long, short, etc.) peuvent être réassignées après leur définition. Ceci peut être évité en utilisant final.

JavaScript , tous les types primitifs (Undefined, Null, Boolean, Number, BigInt, String, Symbol) sont immuables, mais les objets personnalisés sont généralement mutables.

); }const arr = [ 1 , 2 , 3 ]; arr . push ( 4 ); console . log ( arr ); // [1, 2, 3, 4]

L'utilisation d'un état immuable est devenue une tendance croissante en JavaScript depuis l'introduction de React , qui privilégie les modèles de gestion d'état de type Flux tels que Redux .

Perl

En Perl , on peut créer une classe immuable avec la bibliothèque Moo en déclarant simplement tous les attributs en lecture seule :

( is => 'ro', # read only default => 'data', # can be overridden by supplying the constructor with # a value: Immutable->new(value => 'something else'); ); 1; "
package Immutable ; use Moo ;a valeur => ( est => 'ro' , # lecture seule par défaut => 'data' , # peut être surchargé en fournissant au constructeur # une valeur : Immutable->new(valeur => 'autre chose'); );1 ;

La création d'une classe immuable nécessitait auparavant deux étapes : premièrement, la création d'accesseurs (automatiquement ou manuellement) empêchant la modification des attributs de l'objet, et deuxièmement, la prévention de la modification directe des données d'instance des instances de cette classe (celles-ci étaient généralement stockées dans une référence de hachage et pouvaient être verrouillées avec la fonction lock_hash de Hash::Util) :

mk_ro_accessors(qw(value)); use Hash::Util 'lock_hash'; sub new { my $class = shift; return $class if ref($class); die \"Arguments to new must be key => value pairs\ \" unless (@_ % 2 == 0); my %defaults = ( value => 'data', ); my $obj = { %defaults, @_, }; bless $obj, $class; # prevent modification of the object data lock_hash %$obj; } 1; "
package Immutable ; use strict ; use warnings ; use base qw(Class::Accessor) ; # créer des accesseurs en lecture seule __PACKAGE__ -> mk_ro_accessors ( qw(value) ); use Hash::Util 'lock_hash' ;sub new { my $class = shift ; return $class if ref ( $class ); die "Les arguments de new doivent être des paires clé => valeur " unless ( @_ % 2 == 0 ); my %defaults = ( value => 'data' , ); my $obj = { %defaults , @_ , }; bless $obj , $class ; # empêcher la modification des données de l'objet lock_hash %$obj ; } 1 ;

Ou encore, avec un accesseur écrit manuellement :

value pairs\ \" unless (@_ % 2 == 0); my %defaults = ( value => 'data', ); my $obj = { %defaults, @_, }; bless $obj, $class; # prevent modification of the object data lock_hash %$obj; } # read-only accessor sub value { my $self = shift; if (my $new_value = shift) { # trying to set a new value die \"This object cannot be modified\ \"; } else { return $self->{value} } } 1; "
package Immutable ; use strict ; use warnings ; use Hash::Util 'lock_hash' ;sub new { my $class = shift ; return $class if ref ( $class ); die "Les arguments de new doivent être des paires clé => valeur " unless ( @_ % 2 == 0 ); my %defaults = ( value => 'data' , ); my $obj = { %defaults , @_ , }; bless $obj , $class ; # empêcher la modification des données de l'objet lock_hash %$obj ; }# sous - objet d'accès en lecture seule { my $self = shift ; if ( my $new_value = shift ) { # tentative de définition d'une nouvelle valeur die "Cet objet ne peut pas être modifié " ; } else { return $self -> { value } } } 1 ;

PHP

En PHP, les propriétés sont en lecture seule depuis la version 8.1 et les classes en lecture seule depuis la version 8.2.

title = $title; $this->status = $status; } } "
classe BlogData en lecture seule { public string $title ;Statut public $statut ;public function __construct ( string $title , Status $status ) { $this -> title = $title ; $this -> status = $status ; } }

Python

En Python , certains types intégrés (nombres, booléens, chaînes de caractères, tuples, ensembles figés) sont immuables, mais les classes personnalisées sont généralement mutables. Pour simuler l'immuabilité d'une classe, on peut redéfinir la gestion des attributs (définition et suppression) afin de lever des exceptions.

NoReturn: raise TypeError(\"Can not modify immutable instance.\") __delattr__: Callable[[Tuple[Any, ...]], NoReturn] = __setattr__ def __init__(self, x: int, y: int) -> None: # We can no longer use self.value = value to store the instance data # so we must explicitly call the superclass super().__setattr__(\"x\", x) super().__setattr__(\"y\", y) "
from typing import Any , NoReturnclasse ImmutablePoint : Une classe immuable avec deux attributs 'x' et 'y'."""__slots__ : liste [ str ] = [ "x" , "y" ]def __setattr__ ( self , * args : tuple [ Any , ... ]) -> NoReturn : raise TypeError ( "Impossible de modifier une instance immuable." )__delattr__ : Callable [[ Tuple [ Any , ... ]], NoReturn ] = __setattr__def __init__ ( self , x : int , y : int ) -> None : # On ne peut plus utiliser self.value = value pour stocker les données de l'instance # il faut donc appeler explicitement la superclasse super () .__ setattr__ ( "x" , x ) super () .__ setattr__ ( "y" , y )

Les fonctions d'assistance de la bibliothèque standard `int` collections.namedtupleet typing.NamedTuple`int`, disponibles à partir de Python 3.6, permettent de créer des classes immuables simples. L'exemple suivant est globalement équivalent au précédent, avec en plus quelques fonctionnalités similaires aux tuples :

, [ "x" , "y" ])# Le code suivant crée un tuple nommé similaire à la classe Point ( NamedTuple ) ci-dessus : x : int y : int

Introduites dans Python 3.7, dataclassesces fonctionnalités permettent aux développeurs d'émuler l'immuabilité avec des instances figées . Si une classe de données figée est créée, dataclasseselle sera surchargée __setattr__()et __delattr__()une exception sera levée FrozenInstanceErrorlors de son invocation.

Racket se distingue nettement des autres implémentations Scheme en rendant son type de paire principal (« cons cells ») immuable. Il propose en revanche un type de paire mutable parallèle, via `cons` mcons, ` cons` mcar, set-mcar!etc. De plus, de nombreux types immuables sont pris en charge, comme les chaînes de caractères et les vecteurs immuables, et sont largement utilisés. Les nouvelles structures sont immuables par défaut, sauf si un champ est explicitement déclaré mutable, ou si la structure entière l'est.

Le système de propriété de Rust permet aux développeurs de déclarer des variables immuables et de passer des références immuables. Par défaut, toutes les variables et références sont immuables. Les variables et références mutables sont explicitement créées avec le mutmot-clé `mutable`.

Les objets constants en Rust sont toujours immuables.

Scala , toute entité (plus précisément, une liaison) peut être définie comme mutable ou immuable : lors de la déclaration, on utilise val`value` pour les entités immuables et var`variable` pour les entités mutables. Notez que même si une liaison immuable ne peut pas être réassignée, elle peut néanmoins faire référence à un objet mutable et il est toujours possible d'appeler des méthodes de mutation sur cet objet : la liaison est immuable, mais l' objet sous-jacent peut être mutable.

Par exemple, l'extrait de code suivant :

Plus d articles de Worldlex Wiki

Revenez a l index pour explorer davantage de pages sur l histoire, la science, la culture, la geographie et la societe en francais.

Explorer l index