Introduction
The Prototype Pattern is among the Creational Patterns of the Gang Of Four (GOF). To understand this pattern, we first need to understand its intent.
Intent
“Specify the kind of objects to create using a prototypical instance and create a new objects by copying this prototype.” – GOF
The intent of this pattern asks us to create a new object by copying an existing one. There are two kinds of copies, a Shallow Copy and a Deep Copy. We will explain these later in this article. For now just remember we need to copy or clone the fully-initialized object. Now let us understand in detail with a real-world scenario.
Scenario
Assume there is one product-based company that provides value-added services (VAS), like Mobile Recharge, Bill Payment, DTH Recharge and so on. The entire recharge process covers the following procedure:
- The end user (who wants to do recharge in his own mobile) visits the Recharge Store and asks the store keeper to do a recharge, for examle Vodafone top-off of Rs. 100.
- The store keeper sends a request to our company.
- Our company sends a request to Cyberplat, the service provider.
- Cyberplat sends a request to the Vodafone company.
- Vodafone does a recharge and sends a response back to Cyberplat.
- Cyberplat sends the same response to our company and our company to the Recharge Store and finally sends to it to the end user.
The preceding process is called one transaction. For each transaction our company gets some discount from the service provider and shares a part of that discount with the Recharge Store. The discount received from the service provider is called an incoming discount as it is received by our company and the discount shares with the Recharge Store is called an outgoing discount since our company gives it to the Recharge Store. Until now both the discounts are fixed for all the services and operators. Now the company asks his project manager Mr. X to set various discounts for all possible combinations. Mr. X analyzed and defined four parameters to make a combination, in other words Service, Operator, Circle and Provider. Mr. X gave its name as "Product" and asked his developer Mr. Y to set an Incoming Discount and an Outgoing Discount for each product.
Approach 1
Mr. Y has started working on it. First he has created one class "Commission". In this class he has created one parameterized constructor that receives an incoming and an outgoing discount and set in the public variable.
- public class Commission
- {
- public double IncomingCommission;
- public double OutgoingCommission;
-
- public Commission(double IncomingComm, double OutgoingComm)
- {
- this.IncomingCommission = IncomingComm;
- this.OutgoingCommission = OutgoingComm;
- }
- }
In the next step he has created a class "Prototype", in which he has created properties for service, operator, circle, provider and commission. Using a parameterized constructor he receives the preceding values and set in the properties.
- public class Prototype
- {
- private string _Service;
- private string _Operator;
- private string _Circle;
- private string _Provider;
- public Commission Comm;
- public Prototype(string Ser, string Opr, string Cir, string Pro, Commission Comm)
- {
- this._Service = Ser;
- this._Operator = Opr;
- this._Circle = Cir;
- this._Provider = Pro;
- this.Comm = Comm;
- }
-
- public string Provider
- {
- get
- {
- return _Provider;
- }
- set
- {
- _Provider = value;
- }
- }
-
- public string Circle
- {
- get
- {
- return _Circle;
- }
- set
- {
- _Circle = value;
- }
- }
-
- public string Operator
- {
- get
- {
- return _Operator;
- }
- set
- {
- _Operator = value;
- }
- }
-
- public string Service
- {
- get
- {
- return _Service;
- }
- set
- {
- _Service = value;
- }
- }
- }
Client-side Code
The client side has created an object of the class "Prototype" and set the incoming and outgoing discount for each combination of service, operator, circle and provider.
- class Program
- {
- static void Main(string[] args)
- {
- Console.WriteLine(" Service Operator Circle Provider InComm OutComm");
- Console.WriteLine("");
-
- Prototype CPT1 = new Prototype("Topup", "All", "Gujarat", "CyberPlat", new Commission(1.0, 0.5));
- Console.WriteLine("Original Object {0} {1} {2} {3} {4} {5} ", CPT1.Service, CPT1.Operator, CPT1.Circle, CPT1.Provider, CPT1.Comm.IncomingCommission, CPT1.Comm.OutgoingCommission);
-
- Console.WriteLine("");
-
- Prototype CPT2 = new Prototype("Topup", "All", "Telengana", "Euronet", new Commission(0.75, 0.25));
- Console.WriteLine("Original Object {0} {1} {2} {3} {4} {5} ", CPT2.Service, CPT2.Operator, CPT2.Circle, CPT2.Provider, CPT2.Comm.IncomingCommission, CPT2.Comm.OutgoingCommission);
-
- Console.WriteLine("");
- Console.ReadKey();
- }
- }
Output
Finally he built and run the application successfully.
Mr. Y has explained his code to his project manager Mr. X. Mr. X was very happy that the code works fine. Then Mr. X explains the real-world situation. He asked Mr. Y to assume that there are 5 services, 25 operators, 29 circles and 3 providers. In this case our product count will be (5 * 25 * 29 * 3) 10875. You create an object of prototype for each product. That means you need to create an object for 10875 times to set all the discounts. This should not be done, so figure out some solution for this.
Construction is expensive
Approach 2 (Shallow Copy)
Mr. Y worked hard on this and came with the solution. In the commission class he did not change anything.
- public class Commission
- {
- public double IncomingCommission;
- public double OutgoingCommission;
-
- public Commission(double IncomingComm, double OutgoingComm)
- {
- this.IncomingCommission = IncomingComm;
- this.OutgoingCommission = OutgoingComm;
- }
- }
In the next step he has converted a prototype class into an abstract class. He has also added one abstract method "Clone" in the prototype class.
- abstract class Prototype
- {
- private string _Service;
- private string _Operator;
- private string _Circle;
- private string _Provider;
- public Commission Comm;
- public Prototype(string Ser, string Opr, string Cir, string Pro, Commission Comm)
- {
- this._Service = Ser;
- this._Operator = Opr;
- this._Circle = Cir;
- this._Provider = Pro;
- this.Comm = Comm;
- }
-
- public string Provider
- {
- get
- {
- return _Provider;
- }
- set
- {
- _Provider = value;
- }
- }
-
- public string Circle
- {
- get
- {
- return _Circle;
- }
- set
- {
- _Circle = value;
- }
- }
-
- public string Operator
- {
- get
- {
- return _Operator;
- }
- set
- {
- _Operator = value;
- }
- }
-
- public string Service
- {
- get
- {
- return _Service;
- }
- set
- {
- _Service = value;
- }
- }
- public abstract Prototype Clone();
- }
Then he has created one more class "ConcretePrototypeTopup" and inherited the "Prototype" class. In this concrete prototype class he has implemented an abstract method "Clone". In this method he returned the copy of the existing object using the MemberwiseClone method.
- class ConcretePrototypeTopup: Prototype
- {
- public ConcretePrototypeTopup(string Ser, string Opr, string Cir, string Pro, Commission Comm): base(Ser, Opr, Cir, Pro, Comm)
- {
-
- }
-
- public override Prototype Clone()
- {
- return (Prototype) this.MemberwiseClone();
- }
- }
Now let's check what Mr. Y has created so far using a class diagram. Mr. Y has created an abstract class called "Prototype" and one concrete prototype class that implements an abstract method.
Participants
Prototype: An abstract class that defined the method to clone itself.
ConcretePrototype: Concrete class that implements an abstract method to clone itself.
Client: Requires the cloned copy of the object.
Client-side Code
In the client-side:
- He has created an object of a concrete prototype and set the values, consider it as the original object.
- He has create another object by copying the preceding one using MemberwiseClone, consider it as a shallow copy.
- He changed the values of the shallow copy object.
- He checked the values for the original object.
- class Program
- {
- static void Main(string[] args)
- {
- Console.WriteLine(" Service Operator Circle Provider InComm OutComm");
- Console.WriteLine("");
-
- ConcretePrototypeTopup CPT1 = new ConcretePrototypeTopup("Topup", "All", "Gujarat", "CyberPlat", new Commission(1.0, 0.5));
-
- Console.WriteLine("Original Object {0} {1} {2} {3} {4} {5} ", CPT1.Service, CPT1.Operator, CPT1.Circle, CPT1.Provider, CPT1.Comm.IncomingCommission, CPT1.Comm.OutgoingCommission);
- Console.WriteLine("");
-
- ConcretePrototypeTopup CPT2 = (ConcretePrototypeTopup) CPT1.Clone();
-
- Console.WriteLine("Shallow Copy {0} {1} {2} {3} {4} {5}", CPT2.Service, CPT2.Operator, CPT2.Circle, CPT2.Provider, CPT2.Comm.IncomingCommission, CPT2.Comm.OutgoingCommission);
- Console.WriteLine("");
-
- CPT2.Circle = "Telengana";
- CPT2.Provider = "Euronet";
- CPT2.Comm.IncomingCommission = 0.75;
- CPT2.Comm.OutgoingCommission = 0.25;
-
- Console.WriteLine("Change Shallow {0} {1} {2} {3} {4} {5}", CPT2.Service, CPT2.Operator, CPT2.Circle, CPT2.Provider, CPT2.Comm.IncomingCommission, CPT2.Comm.OutgoingCommission);
- Console.WriteLine("");
-
- Console.WriteLine("Original Object {0} {1} {2} {3} {4} {5}", CPT1.Service, CPT1.Operator, CPT1.Circle, CPT1.Provider, CPT1.Comm.IncomingCommission, CPT1.Comm.OutgoingCommission);
- Console.WriteLine("");
-
- Console.ReadKey();
- }
- }
Output
Mr. Y built and run the application successfully. Now please check the 3rd and 4th row in the output very closely. In the 3rd row Mr. Y has changed the values for circle, provider, incoming commission and outgoing commission for the shallow copied object. Then in the 4th row Mr. Y checked the values for the original object. You can see that the values for circle and provider did not change, in other words it matched the original object but the values for the incoming commission and outgoing commission are changed with the shallow copy object. The reason is the MemberwiseClone method creates a shallow copy by creating a new object and then copying the nonstatic fields of the current object to the new object. If a field is a value type then a bit-by-bit copy of the field is done. If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.
Before demonstrating the preceding code to his project manager Mr. X, Mr. Y thought that Mr. X might ask "What if I don't want to affect or change the original object?", hence Mr. Y has created one more sample.
Approach 3 (Deep Copy)
In this approach Mr. Y has implemented an ICloneable interface in the Commission class. In the Clone method he used the MemberwiseClone method for this class object.
- public class Commission : ICloneable
- {
- public double IncomingCommission;
- public double OutgoingCommission;
-
- public Commission(double IncomingComm,double OutgoingComm)
- {
- this.IncomingCommission = IncomingComm;
- this.OutgoingCommission = OutgoingComm;
- }
-
- public object Clone()
- {
- return this.MemberwiseClone();
- }
- }
In the Prototype class, he has converted the commission variable into a property to be defined by its class constructor.
- abstract class Prototype
- {
- private string _Service;
- private string _Operator;
- private string _Circle;
- private string _Provider;
- private Commission _Commission;
- public Prototype(string Ser, string Opr, string Cir, string Pro, Commission Comm)
- {
- this._Service = Ser;
- this._Operator = Opr;
- this._Circle = Cir;
- this._Provider = Pro;
- this.Comm = Comm;
- }
-
- public Commission Comm
- {
- get
- {
- return _Commission;
- }
- set
- {
- _Commission = value;
- }
- }
- public string Provider
- {
- get
- {
- return _Provider;
- }
- set
- {
- _Provider = value;
- }
- }
-
- public string Circle
- {
- get
- {
- return _Circle;
- }
- set
- {
- _Circle = value;
- }
- }
-
- public string Operator
- {
- get
- {
- return _Operator;
- }
- set
- {
- _Operator = value;
- }
- }
-
- public string Service
- {
- get
- {
- return _Service;
- }
- set
- {
- _Service = value;
- }
- }
- public abstract Prototype Clone();
- }
In the concrete prototype class, he has implemented an abstract method "Clone". In this method he has created a copy of members that are value types and then created a new instance of commission by calling its Clone method.
- class ConcretePrototypeTopup: Prototype
- {
- public ConcretePrototypeTopup(string Ser, string Opr, string Cir, string Pro, Commission Comm): base(Ser, Opr, Cir, Pro, Comm)
- {
-
- }
- public override Prototype Clone()
- {
- ConcretePrototypeTopup objCPT = (ConcretePrototypeTopup) this.MemberwiseClone();
- objCPT.Comm = (Commission) this.Comm.Clone();
- return objCPT;
- }
- }
Now let's check what Mr. Y has created so far, using a class diagram. Mr. Y has implemented the ICloneable interface in the Commission class. Mr. Y has also created and an abstract class called "Prototype" and one concrete prototype class that implements an abstract method.
Client-side Code
In the client side:
- He has created an object of concrete prototype and set the values, consider it as the original object.
- He has create another object by copying the preceding one using the Clone abstract method, consider it as a deep copy.
- He changed the values of the deep copy object.
- He checked the values for the original object.
- class Program
- {
- static void Main(string[] args)
- {
- Console.WriteLine(" Service Operator Circle Provider InComm OutComm");
- Console.WriteLine("");
- ConcretePrototypeTopup CPT1 = new ConcretePrototypeTopup("Topup", "All", "Gujarat", "CyberPlat", new Commission(1.0, 0.5));
-
- Console.WriteLine("Original Object {0} {1} {2} {3} {4} {5} ", CPT1.Service, CPT1.Operator, CPT1.Circle, CPT1.Provider, CPT1.Comm.IncomingCommission, CPT1.Comm.OutgoingCommission);
- Console.WriteLine("");
-
- ConcretePrototypeTopup CPT2 = (ConcretePrototypeTopup) CPT1.Clone();
-
- Console.WriteLine("Deep Copy {0} {1} {2} {3} {4} {5}", CPT2.Service, CPT2.Operator, CPT2.Circle, CPT2.Provider, CPT2.Comm.IncomingCommission, CPT2.Comm.OutgoingCommission);
- Console.WriteLine("");
-
- CPT2.Circle = "Telengana";
- CPT2.Provider = "Euronet";
- CPT2.Comm.IncomingCommission = 0.75;
- CPT2.Comm.OutgoingCommission = 0.25;
-
- Console.WriteLine("Change Deep {0} {1} {2} {3} {4} {5}", CPT2.Service, CPT2.Operator, CPT2.Circle, CPT2.Provider, CPT2.Comm.IncomingCommission, CPT2.Comm.OutgoingCommission);
- Console.WriteLine("");
- Console.WriteLine("Original Object {0} {1} {2} {3} {4} {5}", CPT1.Service, CPT1.Operator, CPT1.Circle, CPT1.Provider, CPT1.Comm.IncomingCommission, CPT1.Comm.OutgoingCommission);
- Console.WriteLine("");
- Console.ReadKey();
- }
- }
Output
Mr. Y has built and run the application successfully. Here we can see in the 4th row that the value of the original object is not changed with the deep copy object.
Comparison
Mr. Y has explained both the application code to his project manager Mr. X and has shown the demo by comparing the output of the shallow copy and deep copy. Mr. X was very happy since now there is no need to create an object 10875 times.
Shallow Copy
Deep Copy
Summary
When construction of an object is expensive or we want to hide the constructor, we can use "The Prototype Pattern".