The prototype design pattern is a creational design pattern that allows developers to hide the complexity of making new instances from clients by copying (cloning) an existing object rather than creating a new instance from scratch.
The prototype pattern is all about the creation of objects in a cost-friendly manner. Think about a situation where the creation of objects may take a long time and involve a lot of processes. In such situations, this pattern uses the existing object as a prototype and creates a new object using that. There is a possibility to change the properties of this newly created object as well. Figure 1 depicts an overall UML diagram of this pattern.
Prototype Design Pattern in the Context of Java
Cloneable Interface in Java
In Java, we can use the clone() method of the Cloneable interface to implement this pattern. This is the simplest and easiest way to implement it. This avoids the requirement to use the “New” keyword in the object creation.
Note: Clonnable interface was introduced before the concept of Generics in Java. Therefore Clonnable interface always return a simple object without considering Generics.
To overcome this we may need to use casting at necessary locations.
Shallow Copy vs the Deep Copy
There are 2 main ways to copy an object.
Copy the 1st level references to the new Object. The overall process is faster compared to the deep copy. However, the references of the objects are stored in the original memory address. Therefore developers must be careful when modifying and changing values in a shallow copy as it deals with the original object’s memory addresses.
All the fields and underline structure is copied to the new object. The references in the new object point to new memory addresses. The changes done in this copied object are not reflected in the original one. Therefore Deep copy is safer to use but comparatively slower than Shallow copy.
There are 2 main ways to implement the Prototype Pattern.
- Basic Implementation.
- Prototype Registry Implementation.
Difference: In the prototype Registry Implementation, we create a separate registry class in order to cache the objects for prototyping. In other words, the main purpose of this registry is to maintain a pool of prototype-ready objects inside the memory.
- Prototype: The prototype of the object.
- Prototype Registry: A registry to access the frequently-used prototypes.
- Client: Responsible for using the design pattern by getting prototype instances inside the Registry.
This Design pattern can be used —
- When the object creation is expensive and time-consuming.
- Creation of classes that are specified at runtime.
- When we need to create objects that are similar to already created ones.
- When we need to hide the complexity of creating a new instance from the client.
Let’s try to further understand this pattern with the help of a real-world example.
A new Covid-19 center has been established in the Kandy district near the Central Hospital. This center handles large number of patients on a daily basis. The Doctor in charge requested the company to develop an application which will help them to keep track of the activities related to patients.
Patients in the Covid-19 center have the following facilities —
- Watch Television.
- Play games.
- Read books.
Details of a patient —
- Location (Location of the Home)
What is the problem with this implementation?
The requirement clearly states that we need to handle a large number of patients. Therefore we need to create many patient objects. In the current implementation —
Every time we create a new Patient, we are recreating each of the specific methods in the memory(watchTv(),playGames(), ReadBooks()).
Since we have to handle a large number of patients, we may need to recreate these methods over and over again. This is not very efficient.
So what we can do?
These methods are very generic. They do not need to be specific for each instance. Therefore we can move these methods out to their own object and reference that inside the Patient method.
Now we have solved the problem to some extent as CovidFacilities is created only once in memory. We make a reference to it each time we create a new Patient.
How to improve?
Change: Create an object that will delegate the request to “CovidFacilities” on any fail lookups.
What really happens when we call Tom.playGames()?
Further improvement using the Prototype Instantiation Pattern
Let’s apply this to our existing solution.
We can put the methods(playGames(), watchTv(), readBooks()) to the functions prototye without creating a separate covidFacilities Object.
So now we can see that the Prototypes allow sharing of methods across all instances of a function. Instances are the individual Patients. Instead of having to manage a separate object for all the methods, we can use the prototype object which comes built into the Patient function. This is the prototype instantiation pattern.
Sharing methods across all instances of a function with “New” Keyword
There is another easier built-in way to handle sharing of methods with the help of the “New” Keyword. Let’s try to understand this by rearranging out the previous solution.
If we use the new keyword in front of a function invocation, the JS engine will implicitly do 2 main things automatically.
- Create an object called “this” that delegates to the Patient Prototype.
As per our example: let this = Object.create(Patient.prototype)
- Return the created object.
As per our example: return this
Therefore, we can implement the same previous solution with the help of the “New” Keyword as well. The following URL contains the source code for both solutions ( Without the “new” Keyword and With the “new” Keyword).
Contribute to Damsak/Krish-LP-Training development by creating an account on GitHub.
I have used the following playlist by Mr.Krishntha Dinesh to gather the required information.
The GoF refers to the Prototype pattern as one that creates objects based on a template of an existing object through…