According to GoF, the Definition is:
“Prototype Design Pattern specifies the kind of objects to create using a prototypical instance, and create new objects by copying this prototype”.
If I want to say it In simple words what Prototype Design Pattern is,
create a duplicate object or clone of the existing object. The first thing may come to your mind is why just not creating a new one from scratch.
When it comes to Complicated objects we cant do this approach so whats way?
Here Prototype Design Pattern Shines.
Real-world example
Imagine a factory creates a new phone myphone10. Later they decided to create a new phone called myphone10 PRO.
It is a good idea to build everything from scratch or just reiterate the existing design of myphone10 and reuse it? As you see by reuse existing objects we are saving resources and time.
the Prototype Design Pattern is the same approach in software engineering.
Just a quick reminder
primitive types:
These value types are stored in “stack” memory and these value types are fixed in size. Basically, they store by Value like boolean or int.
non-primitive types:
A variable holds a reference to the value, then that type of data types are reference types like object or class.
Prototype Pattern Implementation in C#
let's see a scenario in a real-world application.
There is a factory that returns different objects based on what input you pass to it.
You need to create a lot of instances of an object and call the factory and receive different objects. In this case, it's better to create one instance of your base object and clone others from it.
Note: To make a variation of one instance it is better to clone instance instead of creating a new one.
We'll have two classes.PhoneSketch
it's just a sketch with some detail also it contains info about Operation System. I don't want to make you engaged with implementation details so I try to make my classes as simple as possible.
public class PhoneSketch
{
public PhoneSketch(string modelName ,string hardware, OperationSystem operationSystem)
{
(this.ModelName, this.Hardware, this.OS) = (modelName, hardware, operationSystem);
}
public string ModelName { get; set; }
public string Hardware { get; set; }
public OperationSystem OS { get; set; }
}
public class OperationSystem
{
public OperationSystem(string name, int version)
{
(this.Name, this.Version) = (name, version);
}
public string Name { get; set; }
public int Version { get; set; }
}
now we create new phone plan.
PhoneSketch myphone10plan = new PhoneSketch("myphone10","Ram 4 & ...", new OperationSystem("Android", 10));
some customers may prefer Ram 6.so we need new plan with the same properties available in myphone10 and improve it.
Let's take a look at obvious stuff that will not work look like for example.
var myphone10PROplan = myphone10plan ;
This Code will fail cause you just copy reference(shallow copy).
in shallow copy, both objects will look at the same block in memory cause they have the same reference to one object, what we really need is a deep copy.
Implement ICloneable Interface
ICloneable
needs a method to be implemented called Clone()
.The clone method creates a new object that is a copy of the current instance.
public class PhoneSketch : ICloneable
{
public PhoneSketch(string modelName, string hardware, OperationSystem operationSystem)
{
(this.ModelName, this.Hardware, this.OS) = (modelName, hardware, operationSystem);
}
public string ModelName { get; set; }
public string Hardware { get; set; }
public OperationSystem OS { get; set; }
public object Clone()
{
return this.MemberwiseClone();
//Or
return new PhoneSketch(this.ModelName, this.Hardware, this.OS);
}
}
Usage
PhoneSketch myphone10plan = new PhoneSketch("myphone10", "Ram 4 & ...", new OperationSystem("Android", 10));
var myphone10PROplan = (PhoneSketch)myphone10plan.Clone();
myphone10PROplan.ModelName = "myphone10 Pro";
myphone10PROplan.Hardware = "Ram 6";
for example hardware store data in the string(obviously it must be an object) but for making implementation easy to understand I set type as a string. for now myphone10plan
and myphone10PROplan
are two different objects, but what we really do its kind of shallow copy or clone. We just make a new object from the existing one but dependent objects still have the same reference.OperationSystem
is one of them in my example. let's see whats will happen if we change one of the dependencies.
myphone10PROplan.OS.Version= 11;
Console.WriteLine(myphone10plan.OS.Version.ToString());//Display 11
Console.WriteLine(myphone10PROplan.OS.Version.ToString());//Display 11
As you can see when comes to dependencies Clone
brokes.This is the main diffrence between Clone
and DeepCopy
.
Prototype Design Pattern required deep copy so ICloneable is not the interface that you should be using.
Note: In most solutions avoiding Clone() is a good plan.
A simple way from C++
In the C++ programming language, a copy constructor is a special constructor for creating a new object as a copy of an existing object.
copy constructor Provide simple API with a constructor and gets the same type as a parameter.
Add new constructor to PhoneSketch
class
public PhoneSketch(PhoneSketch duplicatemodel)
{
(this.ModelName, this.Hardware, this.OS) = (duplicatemodel.ModelName, duplicatemodel.Hardware, new OperationSystem(duplicatemodel.OS));
}
Add new constructor to OperationSystem
class
public OperationSystem(OperationSystem os)
{
(this.Name, this.Version) = (os.Name, os.Version);
}
Usage
var dupmodel = new PhoneSketch(myphone10plan);
you specify constructor for every class, it may make other developers confused while they are creating classes if they don't have any idea about what constructor with the same object parameter is!? It's also not very idiomatic and not be good to be used in .Net.
Deep copy Through Serialization
if you take a Serializer and you serialize your object the serializer should serialize the entire tree. You can use binary or XML serializer but I prefer using JSON. by Newtonsoft.Json
You will have full access to your models and you can customize them.
Here is binary Serializer
sample
public static class PrototypeHelper
{
public static T DeepCopyBinary<T>(this T self)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, self);
ms.Position = 0;
return (T)formatter.Deserialize(ms);
}
}
}
one of the problems with this approach is that you have to add [Serializable]
attribute to all of your classes.as you see XML or Binary Serializerhave some requirements.
in my opinion Json way it much easier and better, cause not always you have access to classes to add an attribute, You just need JSON serializer. In this sample, I'm using Newtonsoft.Json
.
public static T DeepCopyJson<T>(this T self)
{
var serializedobj = JsonConvert.SerializeObject(self);
return JsonConvert.DeserializeObject<T>(serializedobj);
}
conclusion
Now you have a deep view what Prototype design pattern is and different ways of implantation of it.its up to you if you want implement interface or create contractors or just use Serialization.
if you have to create a lot of instances of an object and their same or similar to the main prototypical instance its good to use this pattern.
Thanks for reading and Happy Coding!
Top comments (3)
Nice one. You can use => to make the constructors and methods terser.
Thanks for your tip. Actually my main goal is just the principle.
Thank you for your good article
shervin jon