Una manera simple de lograr esto sería tener una interfaz que le permita a uno leer las propiedades y llamar solo a métodos de solo lectura y una clase que implemente esa interfaz que también le permite escribir esa clase.
Su método que lo crea, trata con el primero y luego devuelve el último proporcionando solo una interfaz de solo lectura para interactuar. Esto no requeriría copia y le permite ajustar fácilmente los comportamientos que desea que estén disponibles para la persona que llama en lugar del creador.
Toma este ejemplo:
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;
}
}
La única desventaja de este enfoque es que uno simplemente podría lanzarlo a la clase implementadora. Si se tratara de una cuestión de seguridad, simplemente usar este enfoque es insuficiente. Una solución para esto es que puede crear una clase de fachada para ajustar la clase mutable, que simplemente presenta una interfaz con la que trabaja la persona que llama y no puede tener acceso al objeto interno.
De esta manera, ni siquiera el casting te ayudará. Ambos pueden derivar de la misma interfaz de solo lectura, pero la conversión del objeto devuelto solo le dará la clase Facade, que es inmutable ya que no cambia el estado subyacente de la clase mutable envuelta.
Vale la pena mencionar que esto no sigue la tendencia típica en la que un objeto inmutable se construye de una vez por todas a través de su constructor. Es comprensible que tenga que lidiar con muchos parámetros, pero debe preguntarse si todos estos parámetros deben definirse por adelantado o si algunos pueden introducirse más adelante. En ese caso, se debe usar un constructor simple con solo los parámetros requeridos. En otras palabras, no use este patrón si está ocultando otro problema en su programa.