Шаблон прототипа - это шаблон творческого проектирования. Это шаблон, который позволяет копировать / клонировать (создавать) экземпляры.
Клонирование, т.е. неглубокое копирование, означает: создается новый экземпляр объекта, но все его поля / ссылки повторно используются / копируются. Данные исходного экземпляра копируются в новый экземпляр. Каждая клонированная ссылка указывает на то же самое место в памяти, что и его исходная ссылка (для ссылочных типов).
В отличие от глубокого клона, т.е. глубокой копии: создается новый экземпляр объекта, но все его поля / ссылки инициализируются новыми экземплярами. Данные исходного экземпляра заменены новыми экземплярами. Каждая клонированная ссылка указывает на другую ячейку памяти, чем ее исходные ссылки.
Клонирование / неглубокая копия полезна, когда внутренние ссылки / поля слишком дороги для создания их новых для каждого экземпляра. Вместо этого вы можете создать неглубокую копию существующего экземпляра (прототипа), чтобы повторно использовать экземпляры объектов, на которые имеются внутренние ссылки. Вы можете кэшировать начальный экземпляр и умножить его путем клонирования. В этом случае вы захотите реализовать шаблон прототипа только с мелкой копией (клоном).
Глубокое клонирование / глубокая копия полезны, когда вы хотите использовать прототип как форму фабрики (Абстрактная фабрика - это также шаблон создания), чтобы удалить зависимость от (конкретного) типа. Если у вас есть класс Employer
, которому необходимо динамически создавать экземпляры, например, Appointment
, вы можете использовать прототип IAppointment
для их создания. Преимущество состоит в том, что вы можете создавать экземпляры, не вводя тесную связь с реализующим типом Appointment
: вместо new Appointment()
, используя конкретный тип, вы теперь вызываете IAppointment.DeepClone()
для абстрактного типа. В этом случае, когда цель состоит в том, чтобы избежать тесной связи с реализациями, вы можете предложить глубокий клон, то есть глубокую копию прототипа.
Ваш пример неверно реализует клонирование. Текущая реализация CloneShallow
возвращает тот же экземпляр (сам):
public Prototype CloneShallow()
{
// Returns itself
return this;
}
Employee prototype = new Employee();
Employee clone = prototype.CloneShallow() as Employee;
// Returns 'true' => the original instance was not copied to a new instance.
bool isTheSameInstance = object.ReferenceEquals(prototype, clone);
Ваш текущий CloneDeep
фактически делает то, что должен делать CloneShallow
: копировать данные в новый экземпляр:
public Prototype CloneDeep()
{
// Creates a new instance that references the same data (wrong)
return new Employee { Appointment = this.Appointment };
}
Employee prototype = new Employee();
Employee clone = prototype.CloneDeep() as Employee;
// Returns 'false' => the original instance was successfully copied to a new instance, ...
bool isTheSameInstance = object.ReferenceEquals(prototype, clone);
// ...but the data is still the same => shallow copy or clone
isTheSameInstance = object.ReferenceEquals(propotype.Appointment, clone.Appointment); // true
Чтобы класс мог создать неглубокую копию / клон самого себя, рекомендуется использовать object.MemberwiseClone()
(ссылка также объясняет разницу между clone / deep clone еще раз).
Правильная и улучшенная реализация шаблона Prototype может выглядеть следующим образом:
public interface IPrototype<T>
{
// Shallow copy.
// Use this if you want to use the prototype to create new instances while saving memory or resources
// by reusing/copying all internal references
public T Clone();
// Use this to create a new instance where all internal references are also new instances.
public T DeepClone();
}
public class Employee : IPrototype<Employee>
{
public IAppointment Appointment { get; set; }
public string FullName { get; set; }
public Employee()
{
this.Appointment = new Appointment();
this.FullName = "Tony Montana";
}
public Employee Clone()
{
// Create a new instance which is a shallow copy of 'this'
return object.MemberwiseClone() as Employee;
}
public Employee DeepClone()
{
// Create a new instance using Clone().
// This way we only need to replace all reference types as value types linke 'int' are new references by language design
var newEmployee = Clone();
// Create new instances of the internal references too
newEmployee.Appointment = new Appointment();
newEmployee.FullName = String.Copy(this.FullName);
return newEmployee;
}
}
Доказательство:
Employee prototype = new Employee();
Employee deepClone = prototype.DeepClone();
// Returns 'false' => the original instance was successfully copied to a new instance, ...
bool isTheSameInstance = object.ReferenceEquals(prototype, deepClone);
// ...and also the data is not the same => deep copy or deep clone
isTheSameInstance = object.ReferenceEquals(prototype.Appointment, deepClone.Appointment); // false