Eine einfache Möglichkeit, dies zu erreichen, wäre eine Schnittstelle, über die man Eigenschaften lesen und nur Nur-Lese-Methoden aufrufen kann, und eine Klasse, die diese Schnittstelle implementiert, mit der Sie auch diese Klasse schreiben können.
Ihre Methode, die es erstellt, verarbeitet das erstere und gibt das letztere zurück, wobei nur eine schreibgeschützte Schnittstelle zur Interaktion bereitgestellt wird. Dies würde kein Kopieren erfordern, und Sie können das Verhalten, das Sie dem Anrufer zur Verfügung stellen möchten, im Gegensatz zum Ersteller auf einfache Weise optimieren.
Nehmen Sie dieses Beispiel:
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;
}
}
Der einzige Nachteil dieses Ansatzes besteht darin, dass er einfach in die implementierende Klasse umgewandelt werden kann. Wenn es um Sicherheit geht, reicht es nicht aus, diesen Ansatz zu verwenden. Eine Problemumgehung besteht darin, dass Sie eine Fassadenklasse erstellen können, um die veränderbare Klasse zu umschließen, die einfach eine Schnittstelle darstellt, mit der der Aufrufer arbeitet und auf das interne Objekt keinen Zugriff hat.
Auf diese Weise hilft Ihnen nicht einmal das Casting. Beide können von derselben schreibgeschützten Schnittstelle abgeleitet werden. Durch das Konvertieren des zurückgegebenen Objekts erhalten Sie jedoch nur die Facade-Klasse, die unveränderlich ist, da sie den zugrunde liegenden Status der umgebrochenen veränderlichen Klasse nicht ändert.
Erwähnenswert ist, dass dies nicht dem typischen Trend folgt, in dem ein unveränderliches Objekt durch seinen Konstruktor ein für allemal konstruiert wird. Verständlicherweise müssen Sie sich möglicherweise mit vielen Parametern befassen, aber Sie sollten sich fragen, ob alle diese Parameter im Voraus definiert werden müssen oder ob einige später eingeführt werden können. In diesem Fall sollte ein einfacher Konstruktor mit nur erforderlichen Parametern verwendet werden. Mit anderen Worten, verwenden Sie dieses Muster nicht, wenn es ein anderes Problem in Ihrem Programm verdeckt.