Design Patterns In C# - Prototype Design Pattern
Join the DZone community and get the full member experience.
Join For FreeIn 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.
So here's our extension method:
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
{
/// <summary>
/// method to perform a deep clone of an arbitrary (unknown at the time of cloning) object
/// </summary>
/// <typeparam name="T">type of object being cloned</typeparam>
/// <param name="obj">object instance being cloned</param>
/// <returns></returns>
public static T DoDeepClone<T>(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<string, EmployeePrototype> _employees = new Dictionary<string, EmployeePrototype>();
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 :)
Opinions expressed by DZone contributors are their own.
Trending
-
Apache Kafka vs. Message Queue: Trade-Offs, Integration, Migration
-
Build a Simple Chat Server With gRPC in .Net Core
-
Building A Log Analytics Solution 10 Times More Cost-Effective Than Elasticsearch
-
Playwright JavaScript Tutorial: A Complete Guide
Comments