In software design, Prototype Design Pattern is a creational pattern that allows us to create objects from existing objects, thus reducing the need for creating new objects from scratch. This design pattern is used when we want to create new objects with the same properties as existing objects, but with different values. In this article, we will discuss the Prototype Design Pattern in detail and how it can be implemented in Java.
What is the Prototype Design Pattern?
The Prototype Design Pattern is a creational pattern that is used to create new objects from existing objects. The existing object acts as a prototype for the new object, and the new object is created by copying the properties of the existing object. This pattern is used when creating new objects from scratch is a time-consuming and resource-intensive process. Instead, the Prototype Design Pattern can be used to create new objects quickly and efficiently by copying the properties of existing objects.
Why do you need the Prototype Design Pattern?
Prototype Design Pattern is useful in scenarios where creating a new object is resource-intensive or complex. Instead of creating a new object from scratch, we can use an existing object as a prototype to create new objects with the same properties. This approach can be useful in situations where:
The creation of an object is time-consuming or expensive, and we need to create multiple instances of the same object quickly and efficiently.
Objects have a complex creation process, and creating new objects from scratch requires a lot of code duplication.
We need to create multiple variants of an object that differ in only a few properties. In this case, we can create a prototype object with the common properties and then clone it to create new objects with varying properties.
Using the Prototype Design Pattern can help to reduce the amount of code needed to create new objects, improve performance, and promote code reuse. It can be particularly useful in situations where object creation is a bottleneck and needs to be optimized.
Implementation Steps
The first thing that strikes the mind when we hear a prototype is a “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 also we need to use the toString() method to print the object.
Book.java
package PrototypeDesign;
public class Book {
private int bid;
private String Bname;
public int getBid() {
return bid;
}
public void setBid(int bid) {
this.bid = bid;
}
public String getBname() {
return Bname;
}
public void setBname(String bname) {
Bname = bname;
}
@Override
public String toString() {
return "Book [bid=" + bid + ", Bname=" + Bname + "]";
}
}
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
package PrototypeDesign;
import java.util.ArrayList;
import java.util.List;
public class BookShop //implementsCloneable
{
private String shopname;
List < Book > book = new ArrayList < Book > ();
public String getShopname() {
return shopname;
}
publicvoidsetShopname(String shopname) {
this.shopname = shopname;
}
public List < Book > getBook() {
return book;
}
publicvoidsetBook(List < Book > book) {
this.book = book;
}
@Override
public String toString() {
return "BookShop [shopname=" + shopname + ", book=" + book + "]";
}
}
Let’s create an object of BookShop.java and now let's print this object.
BookShopTester.java
package PrototypeDesign;
public class BookShopTester {
public static void main(String[] args) {
BookShop bs = new BookShop();
System.out.println(bs);
}
}
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.
public void loadData() {
for (int i = 1; i <= 5; i++) {
Book b = new Book();
b.setBid(i);
b.setBname("Book" + i);
getBook().add(b);
}
}
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)
package PrototypeDesign;
import java.util.ArrayList;
import java.util.List;
public class BookShop //implementsCloneable
{
private String shopname;
List < Book > book = new ArrayList < Book > ();
public String getShopname() {
return shopname;
}
public void setShopname(String shopname) {
this.shopname = shopname;
}
public List < Book > getBook() {
return book;
}
public void setBook(List < Book > book) {
this.book = book;
}
public void loadData() {
for (int i = 1; i <= 10; i++) {
Book b = new Book();
b.setBid(i);
b.setBname("Book" + i);
getBook().add(b);
}
}
@Override
public String toString() {
return "BookShop [shopname=" + shopname + ", book=" + book + "]";
}
}
BookShopTester.java(Updated)
package PrototypeDesign;
public class BookShopTester {
public static void main(String[] args) {
BookShop bs = new BookShop();
bs.setShopname("R Lall Book Depot");
bs.loadData();
System.out.println(bs);
}
}
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]]]
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), and 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 interfaces we have updated BookShop and BookShopTester classes below.
BookShop.java(Updated)
package PrototypeDesign;
import java.util.ArrayList;
import java.util.List;
public class BookShop //implementsCloneable
{
private String shopname;
List < Book > book = new ArrayList < Book > ();
public String getShopname() {
return shopname;
}
public void set Shopname(String shopname) {
this.shopname = shopname;
}
public List < Book > getBook() {
return book;
}
public void setBook(List < Book > book) {
this.book = book;
}
public void loadData() {
for (int i = 1; i <= 10; i++) {
Book b = new Book();
b.setBid(i);
b.setBname("Book" + i);
getBook().add(b);
}
}
@Override
public String toString() {
return "BookShop [shopname=" + shopname + ", book=" + book + "]";
}
@Override
protected BookShop clone() throws CloneNotSupportedException {
//code to achieve deep cloning instead of shallow cloning
BookShop shop = new BookShop();
for (Book b: this.getBook()) {
shop.getBook().add(b);
}
return shop;
}
}
BookShopTester.java(Updated)
package PrototypeDesign;
public class BookShopTester {
public static void main(String[] args) throws CloneNotSupportedException {
BookShop bs = new BookShop();
bs.setShopname("R Lall Book Depot");
bs.loadData();
BookShop bs1 = bs.clone();
bs.getBook().remove(2); //to achieve deep cloning
bs1.setShopname("Khurana Books");
System.out.println(bs);
System.out.println();
System.out.println(bs1);
}
}
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.
Advantages of Prototype Design Pattern
Here are the advantages of the Prototype Design Pattern in Java:
Reduces the need for creating new objects from scratch
Helps to improve performance by creating new objects quickly and efficiently
Promotes code reuse by reducing the amount of code needed to create new objects
Simplifies the creation of complex objects by using an existing object as a prototype
Allows for the creation of multiple variants of an object with different properties by cloning a prototype object
Provides a flexible way to create new objects by modifying the properties of an existing object
Helps to reduce the amount of code duplication needed to create new objects
Usage of Prototype Design Pattern
Here are some usage scenarios of the Prototype Design Pattern in Java:
Creating objects that are expensive to create from scratch
Creating objects that require a lot of time and effort to initialize
Creating objects with complex initialization processes
Creating objects with a high degree of similarity to existing objects
Creating multiple variants of an object with different properties
Creating objects with a dynamic type at runtime
Conclusion
Prototype Design Patterns can help to improve performance, promote code reuse, simplify object creation, and reduce code duplication. The Prototype Design Pattern is particularly useful in situations where object creation is resource-intensive or complex and where multiple instances of the same object need to be created quickly and efficiently. By using the Prototype Design Pattern, developers can create high-quality, scalable, and maintainable code that is easier to modify and extend over time.
Kommentare