0

So I'm learning about design patterns and today I came across Prototype design pattern. I created Prototype interface and it contains 2 methods ( CloneShallow and CloneDeep ). I created trivial class Employee which implements Prototype interface and has 2 properties( Id and FullName ) . The code is pretty straight forward.

public interface Prototype
{
    public Prototype CloneShallow();

    public Prototype CloneDeep();
}

public class Employee : Prototype
{
    public int Id { get; set; }

    public string FullName { get; set; }


    public Employee(int id,string fullName)
    {
        this.Id = id;
        this.FullName = fullName;
    }
    public Prototype CloneShallow()
    {
        return this;
    }

    public Prototype CloneDeep()
    {
        return new Employee(Id, FullName);
    }

    public void printInfo()
    {
        Console.WriteLine($"Id: {Id}");
        Console.WriteLine($"Full name: {FullName}");

    }
}

In main

class Program
    {
        static void Main(string[] args)
        {
            Employee original = new Employee(1, "First Employee");
            Employee copyPrototype = (Employee)original.CloneShallow();
            Employee copyAssigned = original;


            Employee deepCopy = (Employee)original.CloneDeep();
            Employee newDeepCopy = new Employee(original.Id, original.FullName);
            

        }
    }

I created object named original. After that i performed shallow copy with CloneShallow() method, whereas we have objects original and copyPrototype pointing to same address location. After that, I assigned object copyAssigned addres of object named original ( shallow copy aswel) .

In order to make deep copy of an original object, deepCopy object uses CloneDeep() method to allocate new memory space for it and newDeepCopy uses new keyword.

I am little bit confused. copyAssigned object is shallow copied same as copyProtoype (both of them are pointing to a same memory address) and newDeepCopy object is deep copied same as deepCopy object ( when created each of them has unique memory location with respective properties that are copied). I must be missing something, because I don't see the benefits from it.

Can anyone give a real world example of this pattern or atleast explain what is the use of it ?

Finixzz
  • 19
  • 8
  • First of all, your shallow clone isn't even a clone; it's just returning the same instance. Use `object.MemberwiseClone()` for actual shallow copying. ([MSDN reference](https://learn.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=netcore-3.1)). Shallow and deep copy questions are also plentiful on StackOverflow; here's a [starting point](https://stackoverflow.com/questions/184710/what-is-the-difference-between-a-deep-copy-and-a-shallow-copy) that reviews things. – Sean Skelly Aug 03 '20 at 22:07
  • Also, read up on [value types (struct) vs reference types (class)](https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/data-types/value-types-and-reference-types). This is quite important when understanding shallow vs deep copying. A shallow copy (e.g. MemberwiseClone) will have references to the same instance of reference types in memory, but actual copies of value types. – Sean Skelly Aug 03 '20 at 22:13
  • @SeanSkelly Shallow copy is just an object that points to the same memory address as the original one, so I don't see problem with returning same instance ( hence I implemented prototype interface on my own). – Finixzz Aug 03 '20 at 22:15
  • 1
    You said: "Shallow copy is just an object that points to the same memory address as the original one". But that's not what Shallow Copy means. In the context of C# and .NET, a shallow copy returns a _new instance_ of your object, but the properties/fields of that instance are either fully copied (for value types) or the reference is copied (for reference types). `return this;` is not a _copy_ of anything; it's a reference to the same object instance. `return (Prototype)this.MemberwiseClone();` is a copy, but a shallow one. – Sean Skelly Aug 03 '20 at 22:30
  • The Prototype design pattern is only really useful in [particular languages](https://stackoverflow.com/a/58252134/1371329). – jaco0646 Aug 03 '20 at 23:46

1 Answers1

2

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
BionicCode
  • 1
  • 4
  • 28
  • 44
  • Excellent use of concrete working examples and comments. – Sean Skelly Aug 04 '20 at 18:00
  • So basically, shallow copy performs deep copy on value type variables by default and reference type variable is pointing to the original objects reference memory location, whereas deep copy allocates new memory for whole object( including reference type variables ). If I am wrong correct me. – Finixzz Aug 04 '20 at 18:04
  • _"deep copy allocates new memory for whole object( including reference type variables )"_. Exactly. _"shallow copy performs deep copy on value type variables by default and reference type variable is pointing to the original objects reference memory location"_. I think you got it right. But maybe add more precision to this statement. You don't "deep copy" value types. Value types are _always_ copied except you pass them around using `ref` or `out`. – BionicCode Aug 04 '20 at 20:23
  • Shallow copy and deep copy describe how reference types are copied. By default only the reference to an instance is copied, but not the object instance the reference is pointing to. `Employee employee1 = new Employee();`: creates a reference in memory of type `Employee` and named `employee1`. Since the reference is declared inside a scope block `{}` e.g., a method or if-body, this reference is also called a local variable in order to distinguish it from an instance variable (field). – BionicCode Aug 04 '20 at 20:24
  • The expression also creates a new instance of type `Employee` by allocating memory. The allocated memory location is then assigned to the reference `employee1` - the reference now points to the memory location of the instance. `Employee employee2 = employee1;`: creates a reference of type `Employee` named `employee2`. The reference `employee1` is assigned/copied to the reference `employee2` - this is what a shallow copy or method parameter does. Both references (original and copy or `employee1` and `employee2`) point to the same memory location/instance. – BionicCode Aug 04 '20 at 20:24
  • This is where reference types and value types differ: `int value2 = value1;` would create a new memory location and then let `value2`point to it. Instead of the reference to the memory location the complete memory location is copied. – BionicCode Aug 04 '20 at 20:24
  • A deep copy on the other hand allocates new memory for each reference: both references (original and copy or `employee1` and `employee2`) point to different memory locations/instances. The next level is `ref` or `out`. A reference copy declared using `ref` or `out` is treated like an alias of the original reference - they are essentially the _same_ reference. If you let the original reference point to a new memory location using `new` then the alias will follow. – BionicCode Aug 04 '20 at 20:28