Classes et objets immuablesDate de publication : 31/07/2006 I. Introduction II. Créer une classe immuable I. Introduction
Les développeurs Java connaissent tous très bien la notion de classe immuable. Beaucoup ont d'ailleurs pesté contre sa principale représentante, java.lang.String. L'expression classe immuable est en fait un abus de langage, un substitut courant pour objet immuable. Un objet dit immuable est une instance de classe dont les membres exportés (ou visibles, que cela soit par un modificateur d'accès direct ou indirect, protected, public ou package private) ne peuvent être modifiés après création.
Les classes immuables ont de nombreux avantages en leur faveur et leur utilisation simplifie parfois tellement le développement que je voulais écrire ce billet pour vous encourager à en utiliser aussi souvent que possible. De part leur propriété intrinsèque, ces objets ont un seul état. Cette dernière remarque peut vous sembler anodine mais a de nombreuses répercussions très importantes. Voici en vrac les avantages des objets immuables :
Les classes immuables sont particulièrement adaptées à la représentation de types de données abstraits. L'API de Java en contient plusieurs exemples : Integer, Color, BigDecimal, etc. La définition de type abstrait dépend toutefois de votre application. Ainsi, un logiciel affichant le contenu d'un magasin en ligne (livres, musique, DVD, etc.) pourra utiliser des classes immuables pour représenter les articles.
II. Créer une classe immuable
Écrire une classe immuable n'est pas une tâche difficile mais demande beaucoup d'attention pour ne pas exporter indirectement des valeurs. Voici les règles à suivre :
Le dernier point est le plus délicat mais évident avec un exemple :
A première vue, cet exemple est correct puisque le membre theDate est privé, final et jamais exporté. En outre, la valeur renvoyée par toString est immuable. En réalité, theDate est bel et bien exporté, indirectement :
Ce programme affiche Fri Mar 27 00:50:23 PST 1998, notre classe n'est donc pas immuable. Pour résoudre ce problème, il faut réaliser une copie défensive des objets non immuables passes en paramètre :
Cette fois-ci le résultat est bien Mon Mar 27 00:52:18 PST 2006 malgré la modification de l'année. Ce qui est vrai pour les paramètres d'entrée de la classe l'est également pour les valeurs que la classe renvoie à travers ses différentes méthodes. Ainsi, pour ajouter une methode getDate() à notre classe, nous devrons écrire ceci :
Malheureusement, la copie défensive ne suffit pas toujours. Prenez l'exemple suivant :
Ce code semble parfait à première vue mais cache un gros problème potentiel. En effet, il est possible avec un thread de modifier startDate et/ou endDate de maniere à ce que la condition soit validée mais que des valeurs interdites soient conservées par l'objet. Puisqu'il n'est pas possible de prédire le séquencement des opérations multi-thread dans la plupart des environnements, rien ne garantit que le thread principal ne s'arrêtera pas juste après l'exécution du if pour donner la main à un second thread qui modifiera startYear pour que sa valeur soit supérieure a endYear. La bonne stratégie (la seule en fait) est la suivante :
Ce dernier exemple montre bien pourquoi les classes immuables sont souvent intéressantes. Si Date était immuable, nous pourrions simplement ecrire this.startDate = startDate.
Rendez-vous service, utilisez des classes immuables :)
Cette création est mise à disposition sous un contrat Creative Commons (Paternité - Partage des Conditions Initiales à l'Identique).
|