Prototype Design Pattern With Java

Introduction 

 
In this series of learning different design patterns, we came across the most used and famous pattern, the prototype design pattern. As discussed in the last article, we saw what a design pattern is, and what it provides when implemented with the bridge design pattern.
 
A design pattern provides a simple solution to common problems that are faced in day-to-day life by software developers. Three basic types of design patterns exist, which are listed below:
  • Creational Pattern
  • Structural Pattern
  • Behavioral Pattern
Thus, the pattern described here is like a blueprint that you can customize to solve a particular design problem and they only differ by complexity and scalability of the code.
 
In this article, we will learn about the most famous and commonly used design pattern, i.e the prototype design pattern. Let’s get started.
 

Why the Prototype Design Pattern?

 
To start with, what is a prototype design pattern? One should know why we need this design pattern, what are the drawbacks behind the other patterns to need to introduce a new design pattern, and why we need this new pattern in our code.
 
When you have two design patterns that are useful for object creation we can use factory or prototype design patterns.
 
But when to use what? If we want to create an object factory design pattern is the best way, but there are certain scenarios where object creation takes lots of memory,  and lots of time.
 
Let's take an example of an object created with initial values. These values are coming from the database so it will take some time.
 
Let's suppose we want a second object, then why create a new object from the database? A second object can be created from the first object, something like copying the details of the first object or something like cloning the object that is known as a prototype design pattern.
 

What is the Prototype Design Pattern?

 
Prototype Design Pattern is a part of a creational design pattern, as the name suggests it means “the first model” or “the sample/template of any object before the actual object is constructed”.
 
As per the “Gang of Four” definition, the Prototype Design Pattern is used in scenarios where the application needs to create a large number of instances of a class, which have almost the same state or differ very little.
 
In the native language, the prototype design pattern says cloning an existing object instead of creating a new object again.
 

Implementation

 
The first thing that strikes the mind when we hear a prototype is “sample/template of any object before the actual object creation”.
 
So to understand what this prototype design pattern is, let’s assume a case study of a Bookshop.
 
We create a class Book.java with member variables as bid(Book Id) and bname(BookName) and for that, we need getters and setters for the same and also we need to use the toString() method to print the object.
 
Book.java
  1. package PrototypeDesign;  
  2. public class Book {  
  3.     private int bid;  
  4.     private String Bname;  
  5.     public int getBid() {  
  6.         return bid;  
  7.     }  
  8.     public void setBid(int bid) {  
  9.         this.bid = bid;  
  10.     }  
  11.     public String getBname() {  
  12.         return Bname;  
  13.     }  
  14.     public void setBname(String bname) {  
  15.         Bname = bname;  
  16.     }  
  17.     @Override  
  18.     public String toString() {  
  19.         return "Book [bid=" + bid + ", Bname=" + Bname + "]";  
  20.     }  
To open a BookShop, we need to create a class BookShop.java with member variables as shopName and the list of books i.e of type <Book> and for that, we need getters and setters with toString() method to print the object.
 
BookShop.java
  1. package PrototypeDesign;  
  2. import java.util.ArrayList;  
  3. import java.util.List;  
  4. public class BookShop //implementsCloneable  
  5. {  
  6.     private String shopname;  
  7.     List < Book > book = new ArrayList < Book > ();  
  8.     public String getShopname() {  
  9.         return shopname;  
  10.     }  
  11.     publicvoidsetShopname(String shopname) {  
  12.         this.shopname = shopname;  
  13.     }  
  14.     public List < Book > getBook() {  
  15.         return book;  
  16.     }  
  17.     publicvoidsetBook(List < Book > book) {  
  18.         this.book = book;  
  19.     }  
  20.     @Override  
  21.     public String toString() {  
  22.         return "BookShop [shopname=" + shopname + ", book=" + book + "]";  
  23.     }  
  24. }  
Let’s create an object of BookShop.java and now let's print this object.
 
BookShopTester.java
  1. package PrototypeDesign;  
  2. public class BookShopTester {  
  3.     public static void main(String[] args) {  
  4.         BookShop bs = new BookShop();  
  5.         System.out.println(bs);  
  6.     }  
  7. }  
Output
 
BookShop [shopname=null, book=[]]
 
So we don’t want a BookShop which doesn’t have a name and a book list. We should assign some data, add a new method in BookShop.java.
  1. public void loadData() {  
  2.     for (int i = 1; i <= 5; i++) {  
  3.         Book b = new Book();  
  4.         b.setBid(i);  
  5.         b.setBname("Book" + i);  
  6.         getBook().add(b);  
  7.     }  
  8. }  
This method loadData() will add the data to the list and now we got BookShop with a new updated method and BookShopTester with the desired output.
 
BookShop.java(Updated)
  1. package PrototypeDesign;  
  2. import java.util.ArrayList;  
  3. import java.util.List;  
  4. public class BookShop //implementsCloneable  
  5. {  
  6.     private String shopname;  
  7.     List < Book > book = new ArrayList < Book > ();  
  8.     public String getShopname() {  
  9.         return shopname;  
  10.     }  
  11.     public void setShopname(String shopname) {  
  12.         this.shopname = shopname;  
  13.     }  
  14.     public List < Book > getBook() {  
  15.         return book;  
  16.     }  
  17.     public void setBook(List < Book > book) {  
  18.         this.book = book;  
  19.     }  
  20.     public void loadData() {  
  21.         for (int i = 1; i <= 10; i++) {  
  22.             Book b = new Book();  
  23.             b.setBid(i);  
  24.             b.setBname("Book" + i);  
  25.             getBook().add(b);  
  26.         }  
  27.     }  
  28.     @Override  
  29.     public String toString() {  
  30.         return "BookShop [shopname=" + shopname + ", book=" + book + "]";  
  31.     }  
BookShopTester.java(Updated)
  1. package PrototypeDesign;  
  2. public class BookShopTester {  
  3.     public static void main(String[] args) {  
  4.         BookShop bs = new BookShop();  
  5.         bs.setShopname("R Lall Book Depot");  
  6.         bs.loadData();  
  7.         System.out.println(bs);  
  8.     }  
  9. }  
Output
 
BookShop [shopname=R Lal Book Depot, book=[Book [bid=1, Bname=Book1], Book [bid=2, Bname=Book2], Book [bid=3, Bname=Book3], Book [bid=4, Bname=Book4], Book [bid=5, Bname=Book5]]] 
 
Note
If you are thinking that’s all a prototype design pattern, then it’s “NO”.
 
What if we want a new object of BookShop in BookShopTester, we have to create a new object and load the data as done with the object(bs), then it will take some time to load the data.
 
So we have the concept of cloning in java that will copy the object from the old object.
 
To achieve cloning we have to give permission to BookShop.java class to implement an interface Cloneable. Thus, we need to @override the clone method in BookShop.java.
After updating all the methods and interface we have, updated BookShop and BookShopTester classes below.
 
BookShop.java(Updated)
  1. package PrototypeDesign;  
  2. import java.util.ArrayList;  
  3. import java.util.List;  
  4. public class BookShop //implementsCloneable  
  5. {  
  6.     private String shopname;  
  7.     List < Book > book = new ArrayList < Book > ();  
  8.     public String getShopname() {  
  9.         return shopname;  
  10.     }  
  11.     public void set Shopname(String shopname) {  
  12.         this.shopname = shopname;  
  13.     }  
  14.     public List < Book > getBook() {  
  15.         return book;  
  16.     }  
  17.     public void setBook(List < Book > book) {  
  18.         this.book = book;  
  19.     }  
  20.     public void loadData() {  
  21.         for (int i = 1; i <= 10; i++) {  
  22.             Book b = new Book();  
  23.             b.setBid(i);  
  24.             b.setBname("Book" + i);  
  25.             getBook().add(b);  
  26.         }  
  27.     }  
  28.     @Override  
  29.     public String toString() {  
  30.         return "BookShop [shopname=" + shopname + ", book=" + book + "]";  
  31.     }  
  32.     @Override  
  33.     protected BookShop clone() throws CloneNotSupportedException {  
  34.         //code to achieve deep cloning instead of shallow cloning  
  35.         BookShop shop = new BookShop();  
  36.         for (Book b: this.getBook()) {  
  37.             shop.getBook().add(b);  
  38.         }  
  39.         return shop;  
  40.     }  
BookShopTester.java(Updated)
  1. package PrototypeDesign;  
  2. public class BookShopTester {  
  3.     public static void main(String[] args) throws CloneNotSupportedException {  
  4.         BookShop bs = new BookShop();  
  5.         bs.setShopname("R Lall Book Depot");  
  6.         bs.loadData();  
  7.         BookShop bs1 = bs.clone();  
  8.         bs.getBook().remove(2); //to achieve deep cloning  
  9.         bs1.setShopname("Khurana Books");  
  10.         System.out.println(bs);  
  11.         System.out.println();  
  12.         System.out.println(bs1);  
  13.     }  
Output
 
BookShop [shopname=R Lal Book Depot, book=[Book [bid=1, Bname=Book1], Book [bid=2, Bname=Book2], Book [bid=4, Bname=Book4], Book [bid=5, Bname=Book5]]]
BookShop [shopname=Khurana Books, book=[Book [bid=1, Bname=Book1], Book [bid=2, Bname=Book2], Book [bid=3, Bname=Book3], Book [bid=4, Bname=Book4], Book [bid=5, Bname=Book5]]]
 
Note
That’s the power of the prototype method, for any new method we don’t need to load data again from the database as it will lead to more time complexity i.e for the first object we are loading data from the database but for the second object, we are just cloning the object.
 
Here, in the above code bs.getBook().remove(2); will help in checking whether we have one object two references or if we have a copy of the first object; i.e after removing data from the first object the data for the second object remains unchanged and that is deep cloning.
 
Advantage
  • Reduces the need for sub-classing.
  • Hides complexities of creating objects.
  • Add or remove objects at runtime(dynamic).
Usage
  • The cost of object creation is more.
  • Classes are instantiated at runtime.
  • Need a minimum number of classes.

Summary

 
Coming towards the end of this article, we learned how to create a prototype design pattern.
 
What did we learn?
  • What is a Design Pattern?
  • Why the Prototype Design Pattern?
  • What is a Prototype Design Pattern?
  • Implementation
  • Advantage of Prototype Design Pattern
  • Usage of Prototype Design Pattern.
*NOTE
Read all notes in between the article carefully for a better understanding of this Design Pattern.
 
For other articles on design patterns:
  • Singleton Design Pattern here.
  • Factory Design Pattern here.
  • Builder Design Pattern here.
  • Bridge Design Pattern here.
  • The static keyword here.