Prototype Pattern

The Prototype pattern is used when creating an instance of a class is very time-consuming or complex in some way. Then, rather than creating more instances, you make copies of the original instance, modifying them as appropriate. Prototypes can also be used whenever you need classes that differ only in the type of processing they offer i.e. when there are many subclasses that differ only in the kind of objects they create a Prototype Pattern can be used to reduce the number of subclasses by cloning a prototype.

Prototype Design Pattern helps in reducing number of classes. 

In this pattern the catch is "Create objects by cloning". We sometimes create many subclasses that differ only in the kind of objects they create. In place of many subclasses creating different objects, we can have only one subclass that keeps reference to each objects base class and to creates the object, clones the object passed as parameter to the constructor argument of the subclass. Each object implements the clone method so that it can be cloned. We can use Prototype pattern to reduce the number of subclasses by cloning a prototype. 

Cloning can be achieved by implementing ICloneable of the System namespace. The only member of ICloneable interface is Clone method that returns a new instance of a class with the same value as an existing instance.

ICloneable.Clone method signature

[VisualBasic] Function Clone() As Object
[C#] object Clone();

We must understand that the Clone() method does a Shallow copy not Deep copy. So it just returns a reference to the original copy unlike in Deep copy where a duplicate instance is created. Any changes made to the clone will be reflected in the original copy and vice versa. We can implement Deep copy by using ISerializable interface.

One other drawback is that each subclass of Prototype must implement Clone() method. For example, adding clone method may be difficult when classes under consideration already exist. 

In the example I have created an EmpData class that implements ICloneable and ISerializable interfaces, ICloneable interface is required to mark the class as Cloneable and Clone method is implemented. ISerializable interface is used to implement deep copy (clone) for an EmpData Class, the trick I have used it to serialize the EmpData in a file and Deserialize the file and create another EmpData object, that would copy the Emp objects rather then copying their references as Clone method does. 

The EmpData class has two methods GetEmpData and ChangeEmpData that will get the EmpData as a string and change the data of all the Emp classes. Each of the methods can be called and be used to verify the difference between shallow and deep cloning. In shallow cloning if the EmpData is changed then the changes also appear in the clone of the EmpData object, in deep copy once the EmpData object is cloned the changes would not appear in the clone if the data of the cloned object changes.

The construction of the EmpData is time consuming as it reads an XML File and creates Emp objects. 

XML File

<employees>
<employee>
<firstname>ashish</firstname>
<lastname>jaiman</lastname>
</employee>
<employee>
<firstname>jaya</firstname>
<lastname>pandey</lastname>
</employee>
<employee>
<firstname>neeraj</firstname>
<lastname>jaiman</lastname>
</employee>
<employee>
<firstname>ashutosh</firstname>
<lastname>sharma</lastname>
</employee>
<employee>
<firstname>zerman</firstname>
<lastname>billingslea</lastname>
</employee>
<employee>
<firstname>bernd</firstname>
<lastname>burkhardt</lastname>
</employee>
<employee>
<firstname>sanjeev</firstname>
<lastname>bhutt</lastname>
</employee>
<employee>
<firstname>li</firstname>
<lastname>li</lastname>
</employee>
</
employees>

C# Implementation

using System;
using System.Xml;
using System.IO;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable()]
public class CEmpData: ICloneable,ISerializable
{
private ArrayList ArrEmp;
public CEmpData()
{
ArrEmp =
new ArrayList();
InitializeData();
}
private void InitializeData()
{
XmlDocument xmlDoc =
new XmlDocument();
CEmp objEmp;
xmlDoc.Load("empdata.xml");
foreach(XmlNode node in xmlDoc.DocumentElement.ChildNodes)
{
objEmp =
new CEmp();
objEmp.FName = node.SelectSingleNode("firstname").InnerText;
objEmp.LName = node.SelectSingleNode("lastname").InnerText;
ArrEmp.Add (objEmp);
}
}
public CEmpData(SerializationInfo info,StreamingContext context)
{
int intCount=0;
CEmp objEmp;
ArrEmp =
new ArrayList();
intCount = (
int)info.GetValue("emp_count", intCount.GetType());
for (int intIndex = 0;intIndex<intCount;intIndex++)
{
objEmp =
new CEmp(info,context,intIndex);
ArrEmp.Add(objEmp);
}
}
public void GetObjectData(SerializationInfo info,StreamingContext context)
{
CEmp objEmp;
info.AddValue("emp_count", ArrEmp.Count);
for (int intIndex = 0;intIndex<ArrEmp.Count ;intIndex++)
{
objEmp = (CEmp)ArrEmp[intIndex];
objEmp.GetObjectData(info,context,intIndex);
}
}
public object Clone()
{
return this;
}
public object Clone(bool Deep)
{
if(Deep)
return CreateDeepCopy();
else
return Clone();
}
private CEmpData CreateDeepCopy()
{
CEmpData objEmpCopy;
Stream objStream;
BinaryFormatter objBinFormatter =
new BinaryFormatter();
try
{
objStream = File.Open("Empdata.bin", FileMode.Create);
objBinFormatter.Serialize(objStream,
this);
objStream.Close();
objStream = File.Open("Empdata.bin", FileMode.Open);
objEmpCopy = (CEmpData)objBinFormatter.Deserialize(objStream);
objStream.Close();
return objEmpCopy;
}
catch(Exception ex)
{
Console.WriteLine (ex.ToString());
return null;
}
}
public string GetEmpData()
{
string strEmpData="";
for (int intCount = 0;intCount < ArrEmp.Count;intCount++)
{
strEmpData = strEmpData + ((CEmp)ArrEmp[intCount]).FName + "\t" + ((CEmp)ArrEmp[intCount]).LName + "\n";
}
return strEmpData;
}
public void ChangeEmpData()
{
foreach (CEmp objEmp in ArrEmp)
{
objEmp.FName ="FirstName";
objEmp.LName ="LastName";
}
}
}

using
System;
using System.Runtime.Serialization;
public class CEmp
{
private string mstrFName;
private string mstrLName;
public string FName
{
get
{
return mstrFName;
}
set
{
mstrFName =
value;
}
}
public string LName
{
get
{
return mstrLName;
}
set
{
mstrLName =
value;
}
}
public CEmp()
{
}
public CEmp(SerializationInfo info,StreamingContext context, int intIndex)
{
string temp = "temp";
mstrFName = (
string)info.GetValue("emp_fname" +intIndex,temp.GetType());
mstrLName = (
string)info.GetValue("emp_lname" + intIndex,
temp.GetType());
}
public void GetObjectData(SerializationInfo info,StreamingContext context, int intIndex)
{
info.AddValue("emp_fname" + intIndex, mstrFName);
info.AddValue("emp_lname" + intIndex, mstrLName);
}


VB.Net Implementation

Imports System.Xml
Imports System.IO
Imports System.Collections
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
<Serializable()>
Public Class CEmpData
Implements ICloneable, ISerializable
Private ArrEmp As ArrayList
Public Sub New()
Dim xmldoc As New XmlDocument
Dim node As XmlNode
Dim objEmp As CEmp
ArrEmp =
New ArrayList
xmldoc.Load("empdata.xml")
For Each node In xmldoc.DocumentElement.ChildNodes
objEmp =
New CEmp
objEmp.FName = node.SelectSingleNode("firstname").InnerText
objEmp.LName = node.SelectSingleNode("lastname").InnerText
ArrEmp.Add(objEmp)
Next
End Sub
Public Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
Dim
intIndex As Integer
Dim intCount As Integer
Dim objEmp As CEmp
ArrEmp =
New ArrayList
intCount =
CInt(info.GetValue("emp_count", GetType(String)))
For intIndex = 0 To intCount - 1
objEmp =
New CEmp(info, context, intIndex)
ArrEmp.Add(objEmp)
Next
End Sub
Public Function Clone() As Object Implements ICloneable.Clone
Try
Return Me
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Function
Public Function Clone(ByVal Deep As Boolean) As Object
Try
If Deep Then
Return CreateDeepCopy()
Else
Return Clone()
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Function
Private Function CreateDeepCopy() As CEmpData
Dim objEmpCopy As CEmpData
Dim objStream As Stream
Dim objBinFormatter As New BinaryFormatter
Try
objStream = File.Open("Empdata.bin", FileMode.Create)
objBinFormatter.Serialize(objStream,
Me)
objStream.Close()
objStream = File.Open("Empdata.bin", FileMode.Open)
objEmpCopy =
CType(objBinFormatter.Deserialize(objStream), CEmpData)objStream.Close()
CreateDeepCopy = objEmpCopy
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Function
Public Sub GetObjectData(ByVal info As System.Runtime.Serialization.SerializationInfo, _
ByVal context As System.Runtime.Serialization.StreamingContext) Implements System.Runtime.Serialization.ISerializable.GetObjectData
Dim intIndex As Integer
Dim objEmp As CEmp
info.AddValue("emp_count", ArrEmp.Count)
For intIndex = 0 To ArrEmp.Count - 1
objEmp = ArrEmp(intIndex)
objEmp.GetObjectData(info, context, intIndex)
Next
End Sub
Public Function GetEmpData() As String
Dim intCount As Integer
Dim strEmpData As String
For intCount = 0 To ArrEmp.Count - 1
strEmpData = strEmpData &
CType(ArrEmp(intCount), CEmp).FName & Chr(9) &
CType(ArrEmp(intCount), CEmp).LName & Chr(13)
Next
GetEmpData = strEmpData
End Function
Public Sub ChangeEmpData()
Dim objEmp As CEmp
For Each objEmp In ArrEmp
objEmp.FName = "FirstName"
objEmp.LName = "LastName"
Next
End Sub
End Class

Imports
System.Runtime.Serialization
Public Class CEmp
Private mstrFName As String
Private mstrLName As String
Public Property FName() As String
Get
FName = mstrFName
End Get
Set(ByVal Value As String)
mstrFName = Value
End Set
End Property
Public Property LName() As String
Get
LName = mstrLName
End Get
Set(ByVal Value As String)
mstrLName = Value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext, _ByVal intIndex As Integer)
mstrFName =
CStr(info.GetValue("emp_fname" & intIndex, GetType(String)))
mstrLName =
CStr(info.GetValue("emp_lname" & intIndex, GetType(String)))
End Sub
Public Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext, _ ByVal intIndex As Long)
info.AddValue("emp_fname" & intIndex, mstrFName)
info.AddValue("emp_lname" & intIndex, mstrLName)
End Sub
End Class