Click here to Skip to main content
15,886,798 members
Articles / Programming Languages / C#

Design Pattern Series: Creational Patterns: Prototype Pattern

Rate me:
Please Sign up or sign in to vote.
3.28/5 (12 votes)
27 Feb 2017CPOL7 min read 12.3K   10   5
"A Creation Design Pattern which is used to duplicate or clone the resource and time consuming expensive object"

Introduction

The Prototype pattern is considered to be used in the following situations

  1. Each time the object is created, resource intensive data are initialized which affects the performance, for example: calls third party API, gets the JSON response, converts it into POCO object and performs some operations and loads this data into the object.
  2. Assume that the same type of objects share the same resource and time consuming data, for example: In heavy database access, the same database resultsets are needed into all the objects(emp1, emp2,...emp<n>) of the same type "Employee".
  3. The cloned object is noticeably less in state (properties values) changes compared to original object so that the cloned object data can be changed as per needs.

Note

While prototype pattern literally creates memory for object, the object pool patterns reuse the already created objects.

Prototype Pattern

  1. An object is instantiated by cloning the existing original object.
  2. This sophisticated original object is called Prototype object.
  3. An object to be cloned (Prototype) should provide a provision to clone (copy) itself for other objects.
  4. This provision is made possible by exposing the Clone( ) method to the client (any calling code is called client, please do not correlate with the client/server idiom here). It is not mandatory to use the same name as Clone( ), any convenient name can be used.
  5. The client code need not use the "new" keyword to create new object. It just calls the Clone() method in the Concrete Prototype Class object. This Clone() method just returns the reference variable of newly created cloned object with all data loaded.
  6. C# provides the IClonable interface as Abstract Prototype which can be implemented in the Concrete Prototype Class.  IClonable exposes clone() method, but the problem is that the return type of the Clone method is object, so type casting is needed.
  7. The state (property values) of the Cloned object remains the same as Prototype object at the time it is being cloned.
  8. If the Prototype class contains only primitive types, then Shallow Copy is enough.
  9. If the Prototype Class holds inside any reference variable to other object, (for e.g., Employee object holds Address object inside), then Deep Cloning is needed, which means new memory is allocated for the reference object too. Otherwise, the Prototype object and all cloned objects share the same memory for the referred objects. In our case, Address. So any change in Address object by any one object affects all other objects because all share the same memory address.
  10. Each language provides a way to clone an object. In C#, this is carried out by MemberwiseClone().

Participants

  • Prototype: Declares method signature for cloning itself
  • ConcretePrototype: Method implementation of cloning the object itself
  • Client: The code that needs the cloned object and calls the ConcretePrototype object's clone() method

Background

Object Creation

Generally, an object is created for the class by the special keyword "new".

The "new" keyword does three things:

  1. Memory is created on the Heap.
  2. Data is initialized to this newly created object in the memory through constructor (Optional).
  3. The pointer or the reference to the object is returned.

For example:

Image 1

Here, the objEmp is the reference variable which holds the address or reference to the actual object which is residing in the Heap Memory. This reference variable objEmp stays in the Stack Memory.

Resource Expensive Object

Assume that an object "objEmp" holds huge data which has been collected from many resources and complex processing, which has involved heavy performance hits. Now the same data is needed for other objects too for example "objEmp1","objEmp2".."objEmp<n>". These objects should not undergo the same sufferings like the "objEmp" object as the required data is already held in the source object "objEmp". The "objEmp" should be cloned.

Implementing the Prototype Pattern

Manual Cloning

Clone every fields, properties , referece types manually. The problem is that this may take longer time to clone all reference objects, and reference objects inside reference objects and goes on. This method is not advisable to implement.

public class Person : ICloneable
{
   public string Name;
   public Person Spouse;
   public object Clone()
   {
       Person p = new Person();
       p.Name = this.Name;
       if (this.Spouse != null)
           p.Spouse = (Person)this.Spouse.Clone();
       return p;
   }
}

Two Types of cloning

MemberwiseClone()

As per Microsoft Documentation,

"The MemberwiseClone method creates a shallow copy by creating a new object, and then copying the nonstatic fields of the current object to the new object. If a field is a value type, a bit-by-bit copy of the field is performed. If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object."

Shallow Copy

When shallow copy is made to create the clone object from the source object, new memory for the cloned object is created only for value types. If the reference variable is inside the source object, then both the original and the cloned object share the same memory for reference variable object. Any change in value of reference object state affect both the original and cloned object.

Generally, this type of shallow copy of object is accomplished using MemberwiseClone().

Image 2

 

Deep Copy

When deep copy is performed to create the cloned object, separate memory is created for child reference variables along with primitive types. To accomplish that, once again, MemberwiseClone() keyword is should be implemeted and exposed in all the reference classes to create the child reference object and then it is assigned to the cloned object so that new separate memory is created for child reference variable too. This is one way to implement Deep Copy of object with reference types members. There are other ways like Serializatiton, using third party NewtonSoft JsonConvert, Reflection..etc  

Image 3

 

Example using MemberwiseClone()

C#
public interface IEmployee
{
    IEmployee ShallowClone();
    IEmployee DeepClone();
    string GetDetails();
}
public interface IProject
{
    IProject ShallowClone();
}
public class ProjectDetail : IProject
{
    public string ProjectName { get; set; }
    public int Size { get; set; }
    public IProject ShallowClone()
    {
        return (IProject)MemberwiseClone();
    }
}
public class Developer : IEmployee
{
    public string Name { get; set; }
    public ProjectDetail project { get; set; }
    public IEmployee ShallowClone()
    {
        return (IEmployee)MemberwiseClone();
    }
    public IEmployee DeepClone()
    {
        Developer dev = (Developer)this.ShallowClone();
        dev.project = (ProjectDetail)project.ShallowClone();
        return dev;
    }
    public string GetDetails()
    {
        return string.Format("{0}->Project Name:{1}->Project Size:{2}",
            Name,
            project.ProjectName,
            project.Size);
    }

}
class PrototypeClient
{
    static void Main(string[] args)
    {
        Console.WriteLine("Shallow Copy Example:");
        Developer dev1 = new Developer()
        {
            Name = "Joe",
            project = new ProjectDetail()
            {
                ProjectName = "E-Commerce",
                Size = 10
            }
        };
        Developer devCopy1 = (Developer)dev1.ShallowClone();
        devCopy1.Name = "Sam";
        devCopy1.project.ProjectName = "Mobile App";
        devCopy1.project.Size = 8;
        Console.WriteLine(dev1.GetDetails());
        Console.WriteLine(devCopy1.GetDetails());

        Console.WriteLine("\nDeep Copy Example:");
        Developer dev2 = new Developer()
        {
            Name = "Joe",
            project = new ProjectDetail()
            {
                ProjectName = "E-Commerce",
                Size = 10
            }
        };
        Developer devCopy2 = (Developer)dev2.DeepClone();
        devCopy2.Name = "Sam";
        devCopy2.project.ProjectName = "Mobile App";
        devCopy2.project.Size = 8;
        Console.WriteLine(dev2.GetDetails());
        Console.WriteLine(devCopy2.GetDetails());
        Console.ReadKey();
    }
}

Output

Image 4

Explanation

In this program:

  • IEmployee is the Prototype
  • Developer is the ConcretePrototype
  • PrototypeClient is the Client which uses the Prototype to create objects

In the above program, both the shallow copy and deep copy is implemented. The developer class holds the ProjectDetail object as a reference variable. This is called object composition(has - a relationship). The original object dev1 holds name="joe", and it also has the reference variable "project" pointing to the ProjectDetail object. The values for the fields are assigned when declaring the dev1 object. The shallow copy is made from dev1 object (original object) and is assigned to new object devCopy1. All values of the devCopy1 are changed purposefully. As the reference object inside the dev1 and devCopy1 both share the same memory location, the last modified value of the reference object values are set to both the original and cloned object devCopy1. In deep copy mode, the "new" reference object (child object) is created to allocate separate memory for the reference child object "project" for the cloned object.

Deep Clone using Serializatiton

Serialization : Converting the object into binary stream

De-serialization : Converting back from the binary stream to the original object

No need to worry about each and every reference child members cloning.

Example

C#
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace CreationPattern
{
    public class Utility
    {
        public static T DeepClone<T>(T sourceObj)
        {
            if (!typeof(T).IsSerializable)
            {
                throw new ArgumentException("Class must be marked [Serializable]");
            }
            if (Object.ReferenceEquals(sourceObj, null))
            {
                return default(T);
            }
            using (var ms = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(ms, sourceObj);
                ms.Position = 0;
                return (T)formatter.Deserialize(ms);
            }
        }
    }
    [Serializable]
    public class ProjectDetail
    {
        public string ProjectName { get; set; }
        public int Size { get; set; }
    }
    [Serializable]
    public class Developer
    {
        public string Name { get; set; }
        public ProjectDetail project { get; set; }
        public Developer DeepClone()
        {
            return (Developer)Utility.DeepClone<Developer>(this);
        }
        public string GetDetails()
        {
            return string.Format("{0}->Project Name:{1}->Project Size:{2}",
                Name,
                project.ProjectName,
                project.Size);
        }
    }
    class PrototypeClient
    {
        static void Main(string[] args)
        {

            Console.WriteLine("\nDeep Copy Example:");
            Developer dev2 = new Developer()
            {
                Name = "Joe",
                project = new ProjectDetail()
                {
                    ProjectName = "E-Commerce",
                    Size = 10
                }
            };
            Developer devCopy2 = (Developer)dev2.DeepClone();
            devCopy2.Name = "Sam";
            devCopy2.project.ProjectName = "Mobile App";
            devCopy2.project.Size = 8;
            Console.WriteLine(dev2.GetDetails());
            Console.WriteLine(devCopy2.GetDetails());
            Console.ReadKey();
        }
    }
}

Output

Image 5

Explanation

Stream is series of bytes.  MemoryStream is series of bytes, which is going to be dealt with Memory. In case the the serialized object has to be stored in a File and deserialized to the original object, then FileStream can be the choice.

The Source Object is serialized using the BinaryFormatter object's method Serialize() and stored in the form of MemoryStream object. And BinaryFormatter uses its Deserialize() to deserialize the MemoryStream obj and returns the new object.

Deep Clone using NewtonSoft JSON Serialization

Import using Newtonsoft.Json in your project. Use NuGet if do not have it

Image 6

C#
using Newtonsoft.Json;
using System;
namespace CreationPattern
{
    public class Utility
    {
        public static T DeepClone<T>(T sourceObj)
        {
            if (Object.ReferenceEquals(sourceObj, null))
            {
                return default(T);
            }
            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(sourceObj));
        }
    }
    public class ProjectDetail
    {
        public string ProjectName { get; set; }
        public int Size { get; set; }
    }
    public class Developer
    {
        public string Name { get; set; }
        public ProjectDetail project { get; set; }
        public Developer DeepClone()
        {
            return (Developer)Utility.DeepClone<Developer>(this);
        }
        public string GetDetails()
        {
            return string.Format("{0}->Project Name:{1}->Project Size:{2}",
                Name,
                project.ProjectName,
                project.Size);
        }
    }
    class PrototypeClient
    {
        static void Main(string[] args)
        {

            Console.WriteLine("\nDeep Copy Example:");
            Developer dev2 = new Developer()
            {
                Name = "Joe",
                project = new ProjectDetail()
                {
                    ProjectName = "E-Commerce",
                    Size = 10
                }
            };
            Developer devCopy2 = (Developer)dev2.DeepClone();
            devCopy2.Name = "Sam";
            devCopy2.project.ProjectName = "Mobile App";
            devCopy2.project.Size = 8;
            Console.WriteLine(dev2.GetDetails());
            Console.WriteLine(devCopy2.GetDetails());
            Console.ReadKey();
        }
    }
}

Output

Image 7

Explanation

(Developer)Utility.DeepClone<Developer>(this)

This code calls the generic Utility class static method. This method uses the JsonConvert.Serialize () to serialize the object into JSON string and JsonConvert.Deserialize() deserializes the JSON string into new object and returns to the called code.

Note to all readers:

Dear programmers, this is my first article. My goal is to make this Prototype Pattern very clear to you with simple language, but not compromising the in-depth technical details. Please share your feedback. Many design patterns like this are yet to come. Thanks for reading!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
India India
Expertise in Cloud Computing, Azure Ecosystem(Azure Web Jobs, Azure Data Factory[ETL tool], Azure functions, Azure Work Flow, Data Lake, Azure Storage, etc), Data Science, Machine Learning, Data Analytics, Predictive Analytics

Comments and Discussions

 
QuestionPrototype Pattern Pin
Jürgen Stephan21-Feb-17 8:21
Jürgen Stephan21-Feb-17 8:21 
AnswerRe: Prototype Pattern Pin
Ramachandran Murugan21-Feb-17 22:21
Ramachandran Murugan21-Feb-17 22:21 
QuestionWhy is this in "Articles » Desktop Development » List Controls » General" Pin
Zebedee Mason21-Feb-17 5:35
Zebedee Mason21-Feb-17 5:35 
AnswerRe: Why is this in "Articles » Desktop Development » List Controls » General" Pin
Ramachandran Murugan21-Feb-17 21:19
Ramachandran Murugan21-Feb-17 21:19 
QuestionEXcellent Article..Ramachandran Pin
Sheik Abdullah20-Feb-17 22:56
Sheik Abdullah20-Feb-17 22:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.