Prototype Design Pattern…
The prototype design pattern is categorized under the creational design pattern. This pattern is not a very popular one but it is a kind of useful design pattern and there are some situations where creating new objects can be very expensive. In other words when object creation is a costly operation and time-consuming. Therefore this prototype pattern provides the best ways of creating an object and this pattern is carrying a concept is to copy an existing object instead of creating a new object from the scratch. The already existing object that we created for the very first time acts as the prototype and the newly cloned instance is a copy of the original instance but we can do changes to the newly cloned instance whenever needed. We can tell this pattern is used to save costly resources and processing time of a program.
So, now we are aware of if there is a scenario where the creation of object process is expensive or there are a lot of things to do when creating objects or we have to create many more objects from a class and where each object is exactly the same to each other or which have to have small differences, we can use this prototype pattern. When we implement this prototype pattern, we use the “clone()” method which is one of the best available ways to create an object from existing objects. In this cloning process, what it does is, it will create an instance from a class one time and place them in registers. later whenever the new creation request is received, it will get an already existing object from the registry and clone it and give that to the client. When we deal with cloning, as developers we need to make sure in your architecture you need to have a Shallow Copy or Deep Copy.
What are Shallow Copy and Deep Copy?
- Shallow Copy — In here, an instance is created by simply copying the data of all variables of the original object. If data is a reference type, only the reference is copied but not the reference instance itself. Hence, original and clone both refer to the same memory location and due to this, it will create ambiguity and runtime errors dangling pointer. A shallow copy is faster.
- Deep Copy — In here, an instance is created by copying data of all variables including its reference and allocating similar memory resources with the same value to the object. A deep copy is comparatively slow.
❗️❗️❗️Key Note: Shallow copy is quite dangerous sometimes 😨because you get the object to another means you copy some references to your new object and if you modify those references and that could affect the original object.
Prototype Design Pattern Usecase:
Now consider the below scenario and let's try to implement a prototype pattern for the given scenario.
Assume as a programmer, you asked to develop a system for a pizza shop to keep records of pizza details and order details, and imagine in that shop we have different types of pizzas. (in this scenario I only go with Non-Vegetarian Pizza and Vegetarian Pizza types but as you know there are many more types available 😉) Now think if we don’t use a prototype pattern for this scenario then we may need to create a new pizza object each time whenever a client requests a pizza. Since we use the prototype pattern, we don’t need to create instances of Pizza every time🤩.
We have two types of Pizza as NonVegPizza and VegPizza. Both are subclasses of a class called Pizza and we have PizzaRegistry class which is used for the creation and storage of NonVegetarianPizza and VegetarianPizza pizza types in HashMap. so whenever clients request the pizza, they can pass on their preferred pizza type that they want and get a clone of pre-created instances of pizza types.
Let’s look at the implementation of the scenario.
Pizza class is an abstract class and since we need to override clone() method, this class implements the Cloneable interface.
NonVegPizza and VegPizza extend from the Pizza class as below. Here NonVegPizza class has its own attribute and oderPizza() method is overridden in both classes.
Also, I implemented an enum called PizzaType and it stores the types of pizza available. In this scenario, I have only 2 types.
Next is to create PizzaRegistry class and that is the place where you create the object (initial)one-time using a new keyword.
As you can see this PizzaRegistry class has a constructor and it will call the initialize() method and inside that, I directly create 2 different instances from both pizza types and I have implemented HashMap with their class type to hold the objects that are initially created. getPizza() method is there return pizza type object and inside that method, I have called clone() method and that will return cloned object. Since the clone() method returns the object type, we need to do perform casting into the type we want.
As a final step, we have our main class which is called as MainApp and refer to the below.
Ready to see the output of the above scenario 🧐.
Click on the below GitHub link to see the full source code of the above scenario.
Ta-Daaa 😎So here you come to the end of the Prototype pattern discussion.
References: