Background
From my last article, I introduced the builder pattern by using an example of Elizabeth's fun activity.
When Elizabeth wants to make animals, she needs to make a head, body, leg, arm and tail for each animal. She uses the mold tool set to make all the body parts of the animal first, and then puts them together to make an animal.
In her fun activity, Elizabeth uses the builder pattern to build the animals. For example, when she wants to make a monkey, once all the five parts are finished, she will glue them together, and then she will decorate it.
The next day, if she wanted to make the same animal again, she needed to go over the same steps again and again. Finally she was tired of doing this, so she asked me if there is a copy machine that could help her to make a copy of her animals anytime she wants to have one. Well, it seems like Elizabeth also wants to follow the Do Not Repeat Yourself programming rules.
So, it seems the prototype design pattern could satisfy Elizabeth's wish. In this case, Elizabeth can make a copy of her first animal (Prototype animal) and decorate it in different ways. She can also use her copied animal as prototype to make other animals as well.
Introduction
Prototype Design Pattern is also a pattern we use to receive an object instance for a particular class, such as builder and factory pattern. Instead of having a new fresh object every time, we can make a copy of an existed object instantly (object we can use as Prototype) and start using it. In that way, we do not have to repeat the building process for the object we are trying to use. The new copy object is totally independent with the original prototype object, and can be used for any purpose that will not affect the original. There is no limit for copying the existing objects, any existing object can be copied. This article introduces an implementation about how we use the Prototype Design Pattern for Elizabeth's animals.
Prototype Design Pattern Structure
Class Diagram
Implementation Code
Prototype Objects
Animal
Animal
class is a class that we are going to make cloneable. In C# .NET, we inherit ICloneable
interface to make our class a cloneable class. In order to implement the clone()
method, either shallow copy or deep copy could be used. Depends on what we need, we can also select different strategies to implement the shallow copy or deep copy too. However, if we do a shallow copy, all the reference variables in the class will still point the same address which the original prototype object has. The Animal
class contains a constructor which accepts another Animal
as a parameter, I code all the clone processes here so it can be called in its clone()
method to achieve the ability to clone itself. Another way we could make the Animal
to be cloneable is to use Serialize
attribute to make the Animal
class to be serializeable. We could serialize the current object to MemoryStream
and deserialize it back to a new object as the copied object. Please read more details about how to make shallow copy and deep copy through the MSDN web site.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace www.askbargains.com
{
namespace PrototypeDesignPattern
{
public class Animal : ICloneable
{
public Head AHead { get; set; }
public List<leg> Legs { get; set; }
public Tail ATail { get; set; }
public string Name { get; set; }
public Animal()
{
AHead = new Head();
Legs = new List<leg>();
ATail = new Tail();
}
public Animal(Animal aAnimal)
{
Name = aAnimal.Name;
AHead = (Head)aAnimal.AHead.Clone();
Legs = new List<leg>();
foreach (Leg aLeg in aAnimal.Legs)
{
Legs.Add((Leg)aLeg.Clone());
}
ATail = (Tail)aAnimal.ATail.Clone();
}
public void Dispaly()
{
Console.WriteLine("I am a " + Name);
AHead.Display();
foreach (Leg aleg in Legs)
{
aleg.Display();
}
ATail.Display();
Console.WriteLine();
}
#region ICloneable Members
public object Clone()
{
return new Animal(this);
}
#endregion
}
}
}
Head
Head
class is another class that will be declared in our Animal
class. Since it's a reference object in our Animal
class, I also implement the Clone()
method to make itself cloneable when we need to clone it. The difference from the Animal
class is that I used MemberwiseClone()
in Clone()
method to copy itself. .NET MemberwiseClone()
helps us to make a shallow copy of our object. Since I only have Number
(int
type) and Name
(string
is reference, but it's also an immutable type that can't be changed. So there is no difference whether I make a shallow copy or a deep copy for it) in my head class, the MemberwiseClone()
should work just fine for it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace www.askbargains.com
{
namespace PrototypeDesignPattern
{
public class Head : ICloneable
{
public int Number { get; set; }
public string Name { get; set; }
public void Display()
{
Console.WriteLine("I have {0} {1} ", Number, Name);
}
#region ICloneable Members
public object Clone()
{
return this.MemberwiseClone();
}
#endregion
}
}
}
Leg
Same as Head
, Leg
class also inherits ICloneable
to make itself copied.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace www.askbargains.com
{
namespace PrototypeDesignPattern
{
public class Leg : ICloneable
{
public string Side { get; set; }
public string Name { get; set; }
public void Display()
{
Console.WriteLine("This is my {0} {1} ", Side, Name);
}
#region ICloneable Members
public object Clone()
{
return this.MemberwiseClone();
}
#endregion
}
}
}
Tail
Tail
class is another cloneable class which is the same as Head
.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace www.askbargains.com
{
namespace PrototypeDesignPattern
{
public class Tail : ICloneable
{
public string Color { get; set; }
public void Display()
{
Console.WriteLine("My tail is {0}", Color);
}
#region ICloneable Members
public object Clone()
{
return this.MemberwiseClone();
}
#endregion
}
}
}
Client App
From the client side, Elizabeth will build the first animal object. After that, she will just make a copy of her first animal to play with.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using www.askbargains.com.PrototypeDesignPattern;
namespace www.askbargains.com
{
namespace Client
{
class Program
{
static void Main(string[] args)
{
Animal firstAnimal = new Animal();
firstAnimal.Name = "Deer";
firstAnimal.AHead.Name = "Antlers";
firstAnimal.AHead.Number = 2;
Leg leftLeg = new Leg();
leftLeg.Side = "Left";
leftLeg.Name = "Hoove";
Leg rightLeg = new Leg();
rightLeg.Side = "Right";
rightLeg.Name = "Hoove";
firstAnimal.Legs.Add(leftLeg);
firstAnimal.Legs.Add(rightLeg);
firstAnimal.ATail.Color = "Brown";
Animal copiedAnimal =(Animal)firstAnimal.Clone();
Console.WriteLine("Display the first animal");
firstAnimal.Dispaly();
Console.WriteLine("Display the copied animal");
copiedAnimal.Dispaly();
copiedAnimal.Name = "Copied Deer";
copiedAnimal.AHead.Name = "Antlers";
copiedAnimal.AHead.Number = 1;
Console.WriteLine("Display the copied animal after some play.
Elizabeh only made 1 antler for it");
copiedAnimal.Dispaly();
Console.WriteLine("Display the original prototype");
firstAnimal.Dispaly();
Animal secondCopy = (Animal)copiedAnimal.Clone();
secondCopy.Name = "An other copy";
Console.WriteLine("Display the secnod copy animal");
secondCopy.Dispaly();
Console.Read();
}
}
}
}
Once we start our client app, you will see that the copied object is exactly the same as the prototype object (firstAnimal
). Play with the copied object will not effect the original prototype. Cool.
Conclusion
In this article, I demonstrated how we can use the Prototype Pattern to help us to copy an existing object. I also wrote another article for the Builder Design pattern about how to create an instance object.
History
- 23rd September, 2009: Initial post