Un moyen simple d'y parvenir serait d'avoir une interface qui permet de lire les propriétés et d'appeler uniquement des méthodes en lecture seule et une classe qui implémente cette interface qui vous permet également d'écrire cette classe.
Votre méthode qui le crée, traite le premier, puis renvoie le second fournissant uniquement une interface en lecture seule avec laquelle interagir. Cela ne nécessiterait aucune copie et vous permet d'affiner facilement les comportements que vous souhaitez mettre à la disposition de l'appelant plutôt que du créateur.
Prenez cet exemple:
public interface IPerson
{
public String FirstName
{
get;
}
public String LastName
{
get;
}
}
public class PersonImpl : IPerson
{
private String firstName, lastName;
public String FirstName
{
get { return firstName; }
set { firstName = value; }
}
public String LastName
{
get { return lastName; }
set { lastName = value; }
}
}
class Factory
{
public IPerson MakePerson()
{
PersonImpl person = new PersonImpl();
person.FirstName = 'Joe';
person.LastName = 'Schmoe';
return person;
}
}
Le seul inconvénient de cette approche est que l'on peut simplement la convertir en classe d'implémentation. S'il s'agissait d'une question de sécurité, alors simplement utiliser cette approche est insuffisant. Une solution de contournement à cela est que vous pouvez créer une classe de façade pour encapsuler la classe mutable, qui présente simplement une interface avec laquelle l'appelant travaille et ne peut pas avoir accès à l'objet interne.
De cette façon, même le casting ne vous aidera pas. Les deux peuvent dériver de la même interface en lecture seule, mais la conversion de l'objet renvoyé ne vous donnera que la classe Facade, qui est immuable car elle ne change pas l'état sous-jacent de la classe mutable enveloppée.
Il convient de mentionner que cela ne suit pas la tendance typique dans laquelle un objet immuable est construit une fois pour toutes à travers son constructeur. Naturellement, vous devrez peut-être traiter de nombreux paramètres, mais vous devez vous demander si tous ces paramètres doivent être définis à l'avance ou si certains peuvent être introduits plus tard. Dans ce cas, un constructeur simple avec uniquement les paramètres requis doit être utilisé. En d'autres termes, n'utilisez pas ce modèle s'il recouvre un autre problème dans votre programme.