Design Patterns - Prototype

06 april 2022 om 10:00 by ParTech Media - Post a comment

In our previous blogs of the design pattern series, we had seen different design patterns such as Abstract Factory, Factory, Singleton, and Builder, all of which fall under the category of creational design patterns. Today, we will explore the last design pattern under this category called Prototype Design Pattern.

Table of contents

  1. Introduction to Prototype
  2. Understanding Prototype design patterns
  3. Conclusion

Introduction to Prototype

The prototype design pattern helps us to create an object from another object which is already been created. This is also referred to as cloning or copying. When it comes to copying, there are two kinds: Shallow Copy and Deep Copy. But before we jump into copy, it is essential that you understand the two types of variables in C# viz. Value type, and Reference type.

Value type variables are the ones that hold the value of the variable in the memory and use a stack for storing values. E.g., int, float, etc.

Reference type variables are the ones that hold the address of the value in their memory. In this, the address is stored in the stack while the actual value is stored in a heap. E.g., arrays, objects of classes, etc.

Now, consider there is a class that has properties of both reference types and value types. In such cases, when we do a copy/clone, it could be either done as a shallow or deep copy.

Here, shallow copy means copying the contents of an existing object to a new object, where z the value types are stored in different addresses, but the same address of the reference type variables is used in both objects. So, whatever change is made to the reference type variables (in any one of the two objects), it will be updated in both objects.

Whereas in the case of deep copy, the functionality of value types remains the same. For reference types, a new address is created where the values of the existing objects are stored. So, updating the reference type variable in any one of the objects has no impact on the other one.

Understanding Prototype design patterns

Prototype design patterns can be used to achieve the cloning of objects through both shallow and deep copy.

To understand how the prototype pattern can be used to achieve this, let's create an example where first name, last name, and address details are stored in an object. We will observe how the details get updated in shallow and deep copy.

First, create a ShallowCopyPrototype class that inherits the ICloneable interface and has three properties: first name, last name, and address. Here the first name is of type string, the last name is of type string, and the Address is of type Address (create a custom class and have addressline1 and city as properties of it. Have a clone method inside it and return a cloned object of it.)

Inside the Clone method of the ShallowCopyPrototype class, implement the method, which returns the memberwise clone (an in-built method) and also a method to print the details. Similar to the ShallowCopyPrototype, create a DeepCopyPrototype class, where the same set of properties and methods are present. But, the clone method has a different implementation.

The main difference between shallow and deep copy can be understood by updating the details of the Address property in the respective classes. To understand the same, in the main method, create an object shallow copy class and assign the first name, last name and address details. Now, clone the object into another. After creating two objects, print the details present in both objects.

After printing, in the cloned shallow copy object, modify the first name and address line 1 property present in the address object. Since this is a shallow copy, the first name details will be as expected in both the objects but the address detail that was updated in the cloned object will be updated in the parent object as well, which can be observed in the output window.

Similarly, create an object for deep copy and assign the values. Create a deep copy clone for the object and print the information. Now, modify the contents in the cloned object (same as above) and try observing both the objects. Did you notice that both the objects hold different data?

using System;

namespace PARTECH_Prototype
{
    internal class Program
    {
        static void Main(string[] args)
        {
            ShallowCopyPrototype shallowCopy = new ShallowCopyPrototype();
            shallowCopy.FirstName = "Micheal";
            shallowCopy.LastName = "Clarke";
            shallowCopy.Address = new Address()
            {
                AddressLine1 = "MCG Road",
                City = "Melbourne",
            };

            var copiedObject = (ShallowCopyPrototype)shallowCopy.Clone();

            Console.WriteLine("Shallow copy - Original Details");
            shallowCopy.Print();
            Console.WriteLine("Cloned Copy");
            copiedObject.Print();

            copiedObject.FirstName = "Struat";
            copiedObject.Address.AddressLine1 = "SCG Road";

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Shallow Copy - After changes");
            shallowCopy.Print();
            Console.WriteLine("Cloned Copy");
            copiedObject.Print();

            Console.WriteLine(Environment.NewLine);
            DeepCopyPrototype deepCopy = new DeepCopyPrototype();
            deepCopy.FirstName = "Micheal";
            deepCopy.LastName = "Clarke";
            deepCopy.Address = new Address()
            {
                AddressLine1 = "MCG Road",
                City = "Melbourne",
            };
            var copiedDeepObject = (DeepCopyPrototype)deepCopy.Clone();

            Console.WriteLine("Deep copy - Original Details");
            deepCopy.Print();
            Console.WriteLine("Cloned Copy");
            copiedDeepObject.Print();

            copiedDeepObject.FirstName = "Struat";
            copiedDeepObject.Address.AddressLine1 = "SCG Road";

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine("Deep Copy - After changes");
            deepCopy.Print();
            Console.WriteLine("Cloned Copy");
            copiedDeepObject.Print();

            Console.ReadLine();
        }
    }

    public class Address
    {
        public string AddressLine1 { get; set;}
        public string City { get; set;}

        public Address Clone()
        {
            return (Address)this.MemberwiseClone();
        }
    }

    public class ShallowCopyPrototype : ICloneable
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public Address Address { get; set; }

        public object Clone()
        {
            return this.MemberwiseClone();
        }

        public void Print()
        {
            Console.WriteLine("FirstName - " + FirstName);
            Console.WriteLine("LastName - " + LastName);
            Console.WriteLine("Address - " + $"{Address.AddressLine1}-{Address.City}");
        }
    }

    public class DeepCopyPrototype : ICloneable
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public Address Address { get; set; }

        public object Clone()
        {
            DeepCopyPrototype deepCopy = (DeepCopyPrototype)this.MemberwiseClone();
            deepCopy.Address = this.Address.Clone();
            return deepCopy;
        }
        public void Print()
        {
            Console.WriteLine("FirstName - " + FirstName);
            Console.WriteLine("LastName - " + LastName);
            Console.WriteLine("Address - " + $"{Address.AddressLine1}-{Address.City}");
        }
    }

}

Conclusion

And that’s what a Prototype creational design pattern is all about. We have also seen how we can implement it in a real-world scenario. Stay tuned for the next blog on the Design Patterns series.

Nieuwste