Design patterns in C# – part 8 (Prototype pattern)

Role

The Prototype pattern creates new objects by cloning one of a few stored prototypes.

Design

PrototypePattern

Implementation

  • Client -> TestPrototypePattern
  • IPrototype -> IPrototype<T> and abstract class Prototype<T>
  • PrototypeManager -> EmployeeManager
  • Prototype -> Employee
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace PrototypePattern
{
    public interface IPrototype
    {
        T Clone();
        T DeepCopy();
    }

    [Serializable]
    public abstract class Prototype : IPrototype
    {
        public T Clone()
        {
            return (T) MemberwiseClone();
        }

        public T DeepCopy()
        {
            var memoryStream = new MemoryStream();
            var binaryFormatter = new BinaryFormatter();
            binaryFormatter.Serialize(memoryStream, this);
            memoryStream.Seek(0, SeekOrigin.Begin);
            var copy = (T) binaryFormatter.Deserialize(memoryStream);
            memoryStream.Close();
            return copy;
        }
    }
}
using System;

namespace PrototypePattern.TestPrototypePattern
{
    [Serializable]
    public class Employee : Prototype
    {
        public Employee(string name, string surname, CorporationStanding corpStand)
        {
            this.Name = name;
            this.Surname = surname;
            this.CorpStand = corpStand;
        }

        public string Name { get; set; }
        public string Surname { get; set; }
        public CorporationStanding CorpStand { get; set; }

        public override string ToString()
        {
            return String.Concat("\t", this.Name, "\t", this.Surname, "\t -> ", this.CorpStand.Standing);
        }
    }
}
using System;

namespace PrototypePattern.TestPrototypePattern
{
    [Serializable]
    public class CorporationStanding
    {
        public CorporationStanding(string standing)
        {
            this.Standing = String.Concat(standing, ", Apriso Corp.");
        }

        public string Standing { get; set; }

        public override string ToString()
        {
            return this.Standing;
        }
    }
}
using System;
using System.Collections.Generic;

namespace PrototypePattern.TestPrototypePattern
{
    [Serializable]
    public class EmployeeManager : Prototype
    {
        public Dictionary Employees =
            new Dictionary
                {
                    {"Jacek", new Employee("Jacek", "Spolnik", new CorporationStanding("Software Developer"))},
                    {"Kamil", new Employee("Kamil", "Mucha", new CorporationStanding("Software Architect"))},
                    {"Maciek", new Employee("Maciek", "Kuzniar", new CorporationStanding("Database Architect"))}
                };
    }
}
using System;

namespace PrototypePattern.TestPrototypePattern
{
    public class TestPrototypePattern
    {
        internal static void DisplayShallowCopy(Employee original, Employee copy)
        {
            Console.WriteLine("\n\nSHALLOW COPY :");
            DisplayOriginalAndCopy(original, copy);
        }

        internal static void DisplayDeepCopy(Employee original, Employee copy)
        {
            Console.WriteLine("\n\nDEEP COPY :");
            DisplayOriginalAndCopy(original, copy);
        }

        private static void DisplayOriginalAndCopy(Employee original, Employee copy)
        {
            Console.WriteLine(String.Format("Original: {0}", original));
            Console.WriteLine(String.Format("Copy:     {0}", copy));
        }

        public static void Main()
        {
            var employeeManager = new EmployeeManager();

            Console.WriteLine("---------- FIRST EMPLOYEE ----------");

            Employee employeeOne = employeeManager.Employees["Jacek"].Clone();
            DisplayShallowCopy(employeeManager.Employees["Jacek"], employeeOne);

            Console.WriteLine("\n\n\n\t---After change of employee (Name)---");
            employeeOne.Name = "Jack";
            DisplayShallowCopy(employeeManager.Employees["Jacek"], employeeOne);

            Console.WriteLine("\n\n\n\t---After change of employee (CorpStand)---");
            employeeOne.CorpStand.Standing = "Senior software developer, APRISO Corp.";
            DisplayShallowCopy(employeeManager.Employees["Jacek"], employeeOne);

            Console.WriteLine("\n\n\n---------- SECOND EMPLOYEE ----------");

            Employee employeeTwo = employeeManager.Employees["Kamil"].DeepCopy();
            DisplayDeepCopy(employeeManager.Employees["Kamil"], employeeTwo);

            Console.WriteLine("\n\n\n\t---After change of employee (Name)---");
            employeeTwo.Name = "Camille";
            DisplayDeepCopy(employeeManager.Employees["Kamil"], employeeTwo);

            Console.WriteLine("\n\n\n\t---After change of employee (CorpStand)---");
            employeeTwo.CorpStand.Standing = "Senior software architect, APRISO Corp.";
            DisplayDeepCopy(employeeManager.Employees["Kamil"], employeeTwo);
        }
    }
}

OUTPUT:

———- FIRST EMPLOYEE ———-

SHALLOW COPY :
Original:       Jacek   Spolnik  -> Software Developer, Apriso Corp.
Copy:           Jacek   Spolnik  -> Software Developer, Apriso Corp.

—After change of employee (Name)—

SHALLOW COPY :
Original:       Jacek   Spolnik  -> Software Developer, Apriso Corp.
Copy:           Jack    Spolnik  -> Software Developer, Apriso Corp.

—After change of employee (CorpStand)—

SHALLOW COPY :
Original:       Jacek   Spolnik  -> Senior software developer, APRISO Corp.
Copy:           Jack    Spolnik  -> Senior software developer, APRISO Corp.

———- SECOND EMPLOYEE ———-

DEEP COPY :
Original:       Kamil   Mucha    -> Software Architect, Apriso Corp.
Copy:           Kamil   Mucha    -> Software Architect, Apriso Corp.

—After change of employee (Name)—

DEEP COPY :
Original:       Kamil   Mucha    -> Software Architect, Apriso Corp.
Copy:           Camille Mucha    -> Software Architect, Apriso Corp.

—After change of employee (CorpStand)—

DEEP COPY :
Original:       Kamil   Mucha    -> Software Architect, Apriso Corp.
Copy:           Camille Mucha    -> Senior software architect, APRISO Corp.

Use when

You want to:

  • Hide concrete classes from the client.
  • Add and remove new classes (via prototypes) at runtime.
  • Keep the number of classes in the system to a minimum.
  • Adapt to changing structures of data at runtime.

Because:

  • In C# 3.0, cloning by deep copying is absolutely straightforward.

Consider using this pattern:

  • With the Composite pattern, to provide archiving.
  • Instead of the Factory Method pattern, when subclasses start proliferating.

Bibliography

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s