Prototype pattern is a creational design pattern. It's a pattern that allows to copy/clone (create) instances.
Clone i.e. shallow copy means: a new instance of an object is created, but all its fields/references are reused/copied. The data of the original instance is copied to a new instance. Each cloned reference points to the same memory location like its original reference (for reference types).
Opposed to a deep clone i.e. deep copy: a new instance of an object is created, but all its fields/references are initialized with new instances. The data of the original instance is replaced with new instances. Each cloned reference points to a different memory location than its original references.
Clone/shallow copy is useful, when internal references/fields are too expensive to create them new for each instance. Instead you may want to create a shallow copy of an existing instance (the prototype) to reuse the internally referenced object instances.
You can cache an initial instance and multiply it by cloning. In this case you will want to implement the Prototype pattern with the shallow copy (clone) only.
Deep clone/deep copy is useful, when you want to use the prototype as a form of factory (Abstract Factory is also a creational pattern) to remove the dependency on a (concrete) type.
When you have a class Employer
which needs to create instances of e.g. Appointment
dynamically, you can use a prototype of IAppointment
to create them. The benefit is that you are able to create instances without introducing tight coupling to the implementing type Appointment
: instead of new Appointment()
, using the concrete type, you now call IAppointment.DeepClone()
on the abstract type.
In this case, where the goal is to avoid tight coupling to implementations, you can offer a deep clone i.e. deep copy of the prototype.
Your example is implementing the cloning wrong.
The current CloneShallow
implementation returns the same instance (itself):
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);
Your current CloneDeep
actually does what CloneShallow
is supposed to do: copy the data to a new instance:
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
To let a class create a shallow copy/clone of itself, it is recommended to use object.MemberwiseClone()
(the link also explains the difference between clone/deep clone once more).
The correct and improved implementation of the Prototype pattern could look like the following:
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;
}
}
Proof:
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