Skip to main content

Prototype Design Pattern

Problem​

You: Shubh, can you explain the Prototype Design Pattern to me?

Shubh: Sure! Let's understand this with the help of a diagram.

Without Prototype

Without Prototype Design Pattern

Shubh: Here, the client code wants the full name of a specific user. For that, the client makes a request to the server, which fetches the data from the database. Our database has data like {firstName: "Shubh", lastName: "Patel", age: 23, ...}. Once the server gets the data, it processes it by joining firstName and lastName together and returns it to the client so that the client can use it wherever needed.

You: I see. So, what if the client wants to clone the data because it needs the same data again for some other reason? Will we repeat the above process just to get the same data?

Note

This is resource-intensive because we are connecting to a database and processing data on the server side.

Shubh: Good question! We can use the Prototype Design Pattern to clone the data, avoiding this resource-intensive process.

UML​

You: Shubh, can you explain the UML diagram for the Prototype Design Pattern?

Shubh: Absolutely! Let's go through the diagram step by step.

Prototype Design Pattern

UML for Prototype Design Pattern

Shubh: In the UML diagram, we have several key components:

  1. Prototype Interface (DataPrototype):

    • This interface declares the clone method.

    • It ensures that all concrete classes implementing this interface must provide an implementation for the clone method.

  2. Concrete Prototype (Data):

    • This class implements the DataPrototype interface.

    • It provides the specific implementation of the clone method, which creates a new instance of the Data class with the same data.

    • This class represents the structure of data that is actually stored in the database.

  3. Client:

    • This is the class that uses the Prototype Pattern.

    • Instead of creating new instances of Data from scratch, the client can request clones of existing instances.

Code Implementation​

Prototype Interface​

DataPrototype.java
interface DataPrototype {
DataPrototype clone();
}

Concrete Prototype​

Data.java
import java.util.ArrayList;
import java.util.List;

@Getter
@Setter
class Data implements DataPrototype {
private String fullName;
private List<String> roles = new ArrayList<>();

public Data(String fullName, List<String> roles) {
this.fullName = fullName;
this.roles = roles;
}

@Override
public Data clone() {
// Deep copy of the roles list to avoid shared reference issues
return new Data(this.fullName, new ArrayList<>(this.roles));
}
}

Client Code​

Main.java
public class Main {
public static void main(String[] args) {
// Simulating server response (time-consuming & resource-intensive)
DataPrototype originalData = fetchDataFromServer();

// Cloning the data using Prototype Design Pattern
DataPrototype clonedData = originalData.clone();

System.out.println("Cloned data: " + ((Data) clonedData).getFullName());
}


}

Example Comparison​

Without using the Prototype Pattern, cloning might look like this:

Main.java
public class Main {
public static void main(String[] args) {
//Either Fetch again

// Simulating server response (time-consuming & resource-intensive)
DataPrototype originalData = fetchDataFromServer();

//OR

// Manual Cloning
DataPrototype clonedData = new Data();
clonedData.setFullName(originalData.getFullName());
// Setting other fields...

// Both approach are less scalable and clean
}

// Simulated method to fetch data from the server
private static DataPrototype fetchDataFromServer() {
// In a real scenario, this method would fetch data from the server and process it to generate the full name
String firstName = "John";
String lastName = "Doe";
String fullName = firstName + " " + lastName;

// Simulated roles
List<String> roles = Arrays.asList("Admin", "User");
return new Data(fullName, roles);
}
}

In this approach, if our Data class had many attributes, manually setting each attribute would lead to messy code with many lines of setters. Imagine if we wanted to clone it 100 times! Would we have to keep writing setFullName, setThis, setThat? The Prototype Pattern simplifies this by providing a clone() method.

Advantages of the Prototype Design Pattern​

Before watching, make sure you give it a try
  • Resource Efficiency: By cloning existing objects instead of fetching and processing the same data repeatedly, we save server and database resources. This leads to faster performance and reduced load on the backend.

  • Improved Code Quality: Using the clone() method instead of manually creating and setting each attribute reduces boilerplate code. It keeps the code cleaner and less error-prone.

  • Avoiding Constructor Explosion: Similar to the Builder Pattern, the Prototype Pattern helps avoid the need for multiple constructors with different parameters. Instead, we create objects using cloning.

  • Dynamic Object Creation: It allows creating objects dynamically at runtime, which is useful when the object type is determined based on dynamic conditions.