J'ai au cours de quelques-uns des projets mis au point un modèle pour la création d'immuable (readonly) objets et immuable graphes d'objets. Des objets immuables réaliser l'avantage d'être 100% "thread-safe" et peuvent donc être réutilisés dans les threads. Dans mon travail, j'ai très souvent l'utilisation de ce modèle dans les applications Web pour les paramètres de configuration et d'autres objets que j'ai charger et de mettre en cache en mémoire. Les objets mis en cache doit toujours être immuables que vous voulez vous assurer qu'ils ne sont pas modifié de façon inattendue.
Maintenant, vous pouvez facilement concevoir des objets immuables comme dans l'exemple suivant:
public class SampleElement
{
private Guid id;
private string name;
public SampleElement(Guid id, string name)
{
this.id = id;
this.name = name;
}
public Guid Id
{
get { return id; }
}
public string Name
{
get { return name; }
}
}
C'est très bien pour de simples classes - mais pour les plus complexes des classes je n'ai pas envie de la notion de transmission de toutes les valeurs par le biais d'un constructeur. Avoir des poseurs sur les propriétés est plus souhaitable et votre code de la construction d'un nouvel objet, il devient plus facile à lire.
Alors, comment voulez-vous créer des objets immuables avec les poseurs?
Eh bien, dans mon modèle, les objets commencent comme étant entièrement mutable jusqu'à ce que vous les congeler avec un seul appel de méthode. Une fois qu'un objet est gelé, il restera immuable à jamais - il ne peut pas être transformé en un objet mutable nouveau. Si vous avez besoin d'une mutable version de l'objet, il vous suffit de le cloner.
Ok, maintenant, sur une partie du code. J'ai dans les extraits de code suivants essayé de faire bouillir le modèle réduit à sa forme la plus simple. Le IElement est l'interface de base que tous les objets immuables doivent en fin de compte mettre en œuvre.
public interface IElement : ICloneable
{
bool IsReadOnly { get; }
void MakeReadOnly();
}
L'Élément de la classe est l'implémentation par défaut de la IElement interface:
public abstract class Element : IElement
{
private bool immutable;
public bool IsReadOnly
{
get { return immutable; }
}
public virtual void MakeReadOnly()
{
immutable = true;
}
protected virtual void FailIfImmutable()
{
if (immutable) throw new ImmutableElementException(this);
}
...
}
Nous allons refactoriser le SampleElement classe ci-dessus pour mettre en œuvre les lois immuables de l'objet modèle:
public class SampleElement : Element
{
private Guid id;
private string name;
public SampleElement() {}
public Guid Id
{
get
{
return id;
}
set
{
FailIfImmutable();
id = value;
}
}
public string Name
{
get
{
return name;
}
set
{
FailIfImmutable();
name = value;
}
}
}
Vous pouvez maintenant modifier la propriété Id et le Nom de la propriété, tant que l'objet n'a pas été marquée comme immuable par l'appel de la MakeReadOnly() la méthode. Une fois qu'il est immuable, en appelant un setter permettra d'obtenir un ImmutableElementException.
Note finale: Le modèle complet est plus complexe que les extraits de code présentés ici. Il contient également un soutien pour les collections d'objets immuables et complète les graphes d'objets immuables graphes d'objets. Le modèle complet vous permet de transformer l'ensemble d'un graphe d'objets immuables par l'appel de la MakeReadOnly() la méthode la plus externe de l'objet. Une fois que vous commencez à créer de plus grands modèles d'objet à l'aide de ce modèle, le risque de fuites d'objets augmente. Une fuite de l'objet est un objet qui ne parvient pas à appeler le FailIfImmutable() la méthode avant de faire une modification à l'objet. Pour vérifier la présence de fuites, j'ai également développé un générique détecteur de fuite de classe pour une utilisation dans les tests unitaires. Il utilise la réflexion pour tester si toutes les propriétés et méthodes de jeter le ImmutableElementException dans l'état immuable. En d'autres termes TDD est utilisé ici.
J'ai appris à l'aimer, ce modèle de beaucoup de choses et de trouver de grands avantages à elle. Donc ce que je voudrais savoir c'est si l'un de vous à l'aide des modèles similaires? Si oui, connaissez-vous des bonnes ressources que du document? Je suis essentiellement la recherche d'améliorations possibles et pour tous les standards qui pourrait déjà exister sur ce sujet.