Tuesday, 10 July 2018

Prototype Design Pattern

  • Like cloning => to duplicate the object
  • New operator is considered harmful
With Prototype design pattern, based on the requirement situations we can save memory by cloning the objects

Example : Adapting to clone an object which consists of many strings (immutable) is a good idea than creating an object

Shallow: when changing clone, the parent will change too. Same memory location
  • MemberwiseClone creates shallow copy
    • Copies non-static fields of the object to new object
    • Value type: bit by bit copy is performed
    • Reference type: reference is copied, not the object
Deep: separated, clone change will not affect the parent. New memory location


ICloneable Interface:
  • Creates a copy of existing object
  • Has clone method (one member) which provides support beyond MemberwiseClone

Sample Code Download: PrototypeDesignPattern.zip (Visual Studio 2017 Console App)

Implementing Shallow Copy

 

Address Class


    public class Address
    {
        public int DoorNumber { get; set; }
        public int StreetNumber { get; set; }
        public int Zipcode { get; set; }
        public string Country { get; set; }
        public override string ToString()
        {
            var stringBuilder = new StringBuilder();
            stringBuilder.Append("AddressDetails :: ");
            stringBuilder.AppendFormat($"Door : {DoorNumber},");
            stringBuilder.AppendFormat($"Street: {StreetNumber},");
            stringBuilder.AppendFormat($"ZipCode : {Zipcode.ToString()},");
            stringBuilder.AppendFormat($"Country : {this.Country}");
            return stringBuilder.ToString();
        }
    }


Employee Class


Create a partial Employee class, in the next step we will implement ICloneable interface

    public partial class Employee
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public int DepartmentId { get; set; }
        public Address AddressDetails { get; set; }

        public override string ToString()
        {
            var stringBuilder = new StringBuilder();
            stringBuilder.AppendFormat($" Name : {Name}");
            stringBuilder.AppendFormat($" DepartmentId : {DepartmentId}");
            if (AddressDetails != null)
            {
                stringBuilder.AppendFormat($" {AddressDetails.ToString()}");
            }
            return stringBuilder.ToString();
        }
    }

Implement ICloneable 



Implement ICloneable clone method using MemberwiseClone method

    public partial class Employee : ICloneable
    {
        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }


Implement Shallow Copy in Program.cs


Implement ShallowCopy function in program.cs and then call it in the main.

        static void Main(string[] args)
        {
            //Shallow Copy - without address (ref)
            ShallowCopy();

            Console.ReadLine();
        }

        public static void ShallowCopy()
        {
            //John
            var empJohn = new Employee
            {
                Id = Guid.NewGuid(),
                Name = "John",
                DepartmentId = 150
            };
            Console.WriteLine(empJohn.ToString());
            Console.WriteLine("--------");

            //Sam: Clone John
            var empSam = (Employee) empJohn.Clone();
            empSam.Name = "Sam";
            Console.WriteLine(empSam.ToString());
            Console.WriteLine("--------");

            //Change the John information and then re-print
            Console.WriteLine("Changed Johns DepatmentId to 161 from 150");
            empJohn.DepartmentId = 161;
            Console.WriteLine(empJohn.ToString());
            Console.WriteLine(empSam.ToString());

        }


Run the program and notice that change in department doesn't change the target.



Now create another method to test address (ref) object and observe the output. Change in address details has propagated to address of target object. This means, MemberwiseClone copies the reference and not the address object itself

        static void Main(string[] args)
        {
            //Shallow Copy - without address (ref)
            //ShallowCopy();

            //Shallow Copy - with ref
            ShallowCopyRef();

            Console.ReadLine();
        }

        public static void ShallowCopyRef()
        {
            //John
            var empJohn = new Employee
            {
                Id = Guid.NewGuid(),
                Name = "John",
                DepartmentId = 150,
                AddressDetails = new Address
                {
                    DoorNumber = 10,
                    StreetNumber = 20,
                    Zipcode = 64113,
                    Country = "US"
                }
            };
            Console.WriteLine(empJohn.ToString());
            Console.WriteLine("--------");

            //Sam: Clone John
            var empSam = (Employee)empJohn.Clone();
            empSam.Name = "Sam";
            empSam.DepartmentId = 151;
            empSam.AddressDetails.StreetNumber = 21;
            empSam.AddressDetails.DoorNumber = 11;
            Console.WriteLine(empSam.ToString());
            Console.WriteLine("--------");

            //Change the John information and then re-print
            Console.WriteLine("Modified John information");
            empJohn.DepartmentId = 161;
            empJohn.AddressDetails.StreetNumber = 30;
            empJohn.AddressDetails.DoorNumber = 13;
            Console.WriteLine(empJohn.ToString());
            Console.WriteLine(empSam.ToString());
            Console.WriteLine("--------");

            //Change the sam information and then re-print
            Console.WriteLine("Modified Sam information");
            empSam.DepartmentId = 171;
            empSam.AddressDetails.StreetNumber = 50;
            empSam.AddressDetails.DoorNumber = 15;
            Console.WriteLine(empJohn.ToString());
            Console.WriteLine(empSam.ToString());
        }
    }



Implementing Deep Copy


CloneablePrototype Abstract Class



Implement the custom CloneablePrototype implementation as shown below. You'll need to add Newtonsoft.Json nuget package.

    public abstract class CloneablePrototype
    {
        // Shallow copy
        public T Clone()
        {
            return (T)this.MemberwiseClone();
        }

        // Deep Copy
        public T DeepCopy()
        {
            string result = JsonConvert.SerializeObject(this);
            return JsonConvert.DeserializeObject(result);
        }
    }



and inherit EmployeeDeep Class from above abstract class

    public partial class EmployeeDeep : CloneablePrototype
    {
        ...
    }


Implementing Deep Copy in Program.cs


Implement as follows and then look at the output.

        static void Main(string[] args)
        {
            //Shallow Copy - without address (ref)
            //ShallowCopy();

            //Shallow Copy - with ref
            //ShallowCopyRef();

            //DeepCopy
            DeepCopy();

            Console.ReadLine();
        }

        public static void DeepCopy()
        {
            //John
            var empJohn = new EmployeeDeep
            {
                Id = Guid.NewGuid(),
                Name = "John",
                DepartmentId = 150,
                AddressDetails = new AddressDeep()
                {
                    DoorNumber = 10,
                    StreetNumber = 20,
                    Zipcode = 64113,
                    Country = "US"
                }
            };
            Console.WriteLine(empJohn.ToString());
            Console.WriteLine("--------");

            //Sam: Clone John
            var empSam = (EmployeeDeep) empJohn.DeepCopy();
            empSam.Name = "Sam";
            empSam.DepartmentId = 151;
            empSam.AddressDetails.StreetNumber = 21;
            empSam.AddressDetails.DoorNumber = 11;
            Console.WriteLine(empSam.ToString());
            Console.WriteLine("--------");

            //Change the John information and then re-print
            Console.WriteLine("Modified John information");
            empJohn.DepartmentId = 161;
            empJohn.AddressDetails.StreetNumber = 30;
            empJohn.AddressDetails.DoorNumber = 13;
            Console.WriteLine(empJohn.ToString());
            Console.WriteLine(empSam.ToString());
            Console.WriteLine("--------");

            //Change the sam information and then re-print
            Console.WriteLine("Modified Sam information");
            empSam.DepartmentId = 171;
            empSam.AddressDetails.StreetNumber = 50;
            empSam.AddressDetails.DoorNumber = 15;
            Console.WriteLine(empJohn.ToString());
            Console.WriteLine(empSam.ToString());
        }

Run the application and notice that the change in address details in source object doesn’t impact the Target object.



Now change to Clone in DeepCopy method and observe the output. What do you see?

//Sam: Clone John
var empSam = (EmployeeDeep) empJohn.Clone();