Pages

Tuesday, March 6, 2012

Design Patterns In C# - Prototype Design Pattern

In my last entry we looked at the Singleton design pattern, in which it allows on a single instance of a class to be created. In this entry we will look at another design pattern, the Prototype design pattern.

What is it:
The Prototype design pattern is a pattern where new instances of a class are created by cloning an initial object. Reasons for using this design pattern are (but not limited to):
  • Avoid the overhead of creating a new object using the [i]new[/il] keyword. This is helpful when there's a serious overhead of creating new objects.
  • Avoid subclasses
  • Use one isntance for the basis of future instances.
When we are not in a situation in which we can call an objects constructor directly, we could clone a pre-existing instance of the object (our prototype). Rather than creating more and more isntances of said object it's possible to save overhead by cloning an existing copy of your object.

Shallow & Deep Clone - Implement
When working with this design method you must create a class using the abstract modifier, meaning that it will be the only class your object will implement/inherit from. One thing to consider when using this design pattern is determining whether we want a deep clone or shallow clone.

Shallow clone: can be performed with using the Object.MemberWise Clone Method, which copies the nonstatic fields of the original object. With reference types the reference is copied meaning the original and cloned object reference to the same instance, so if a value is changed in the original object the shallow cloned object with be updated.

Deep Clone: A deep clone copies both reference and value types, giving 2 distinct instances of an object, so if object A is modified the cloned object (object B) remains unchanged. While this may be preferred in some cases, remember that there is more overhead when performing a deep clone.

For the purpose of this example we will give the option for either a deep or shallow clone of an object, but remember to make sure you know what kind of clone you need before performing the clone.

For ease of use in this example I have created a small extension method to handle to deep clone functionality since there really isnt a built-in method in the .Net framework for performing a deep clone. This method will work for any object type as it doesnt expect a certain type (it expects something of type T, which can be any serializable arbitrary type you wish). We will be cloning via Serialization, keep in mind there are several ways to deep clone an object, including Reflection and IL (Intermediate Language).

NOTE: With this method of cloning you need to add the SerializableAttribute attribute to the object you want to clone with serialization.

using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace DZoneArticles.PrototypeDesignPattern
{
    public static class DeepCloneExtension
    {
        ///

        /// method to perform a deep clone of an arbitrary (unknown at the time of cloning) object
        ///

        /// type of object being cloned
        /// object instance being cloned
        ///
        public static T DoDeepClone(this T obj)
        {
            //make sure the object being passed is serializable, otherwise throw an
            //exception
            if (!obj.GetType().IsSerializable)
                throw new ArgumentException("The object provided is not serializable. Please add the [Serializable()]

attribute to your object", "obj");

            // check for a null object, if found the return the defaults
            // for the object
            if (obj == null)
                return default(T);
            else
            {
                BinaryFormatter bf = new BinaryFormatter();

                using (MemoryStream ms = new MemoryStream())
                {
                    bf.Serialize(ms, obj);
                    ms.Seek(0, SeekOrigin.Begin);
                    return (T)bf.Deserialize(ms);
                }
            }
        }
    }
}
Implement Ptototype Design Pattern
Now that this is out of the way let's go into the Prototype Design pattern. For this there will be three classes in play:
  • EmployeePrototype: Declares an interface for cloning itself
  • Employee: The actual class we'll be prototyping
  • EmployeeManager: This class will manage the interaction between our class (Employee), the prototype (EmployeePrototype) and the client.
So let's construct a small sample application showing how the Prototype Design Process works. First we create EmployeePrototype Class. This class will be marked as abstract, meaning it will be the only class our Employee class can inherit from. So here is our protypical class:
using System;

namespace DZoneArticles.PrototypeDesignPattern
{
    public abstract class EmployeePrototype
    {
        public abstract EmployeePrototype Clone(bool deepClone);
    }
}
Now wasnt that simple, an abstract class with a single abstract method Clone, it is in that method when we determine if a deep or shallow clone is being used (which is why we pass a bvoolean value to it). Our EmployeeManager class will give us an indexer and allow us to access employees by a specified string index. Here's EmployeeManager.cs:
namespace DZoneArticles.PrototypeDesignPattern
{
    public class EmployeeManager
    {
        public Dictionary _employees = new Dictionary();

        public EmployeePrototype this[string idx]
        {
            get { return _employees[idx]; }
            set { _employees.Add(idx, value); }
        }
    }
}
Now for our final class in the prototype pattern. This will be our actual employee class. Since we're going with a simple example the class will have 2 properties: First Name & Last Name. It will also have the abstract method Clone from EmployeePrototype, and will determine, by the value passed to it, whether we're doing a deep or shallow clone:

using System;
using System.Collections.Generic;
using System.IO;

namespace DZoneArticles.PrototypeDesignPattern
{
    [Serializable()]
    public class Employee : EmployeePrototype
    {
        private string _firstName;
        private string _lastName;

        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }

        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; }
        }

        public Employee(string fName, string lName)
        {
            this._firstName = fName;
            this._lastName = lName;
        }

        public override EmployeePrototype Clone(bool deepClone)
        {
            switch (deepClone)
            {
                case true:
                    return this.DoDeepClone() as EmployeePrototype;
                case false:
                    return this.MemberwiseClone() as EmployeePrototype;
                default:
                    return this.MemberwiseClone() as EmployeePrototype;
            }
        }
    }
}
So we have our prototype design pattern set up, let's see how it works when we put it into action:
static void Main(string[] args)
{
    EmployeeManager employee = new EmployeeManager();

    employee["John"] = new Employee("John", "Smith");
    employee["Bill"] = new Employee("Bill", "Jones");

    //now let's clone 'John Smith'
    Employee employee1 = employee["John"].Clone(true) as Employee;

    Console.ReadKey();
}
So we created 2 new employees via the EmployeeManager class. With the constructor we set it's values (first name & last name). We then created a new employee object by cloning the first employee we created. Now we can modify this new employee and not have it affect the original copy since we did a deep clone. Had we went with the shallow clone if we modified the cloned emp,oyee it would have also updated the original employee we created.

So that's how the Prototype Design Pattern works and how you can implement it in C#. While this is a real simple example it definitely can be put into action with more complex objects and situations. Thanks for reading and happy coding :)

No comments: