Prototype Pattern - Java - Explained

Prototype Pattern - Java - Explained

Intent

The Prototype design pattern is a creational pattern that allows you to create new objects by cloning existing ones, rather than by creating them from scratch.

The intent of the Prototype pattern is to specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

Motivation behind the pattern/problem it solves?

The motivation behind the Prototype pattern is to provide a way to create new objects while minimizing the use of system resources and increasing performance. For example, imagine an e-commerce website that needs to display a catalog of products. Each product in the catalog might have multiple properties such as name, description, price, and image. Creating a new instance of each product for each user request can be time-consuming and resource-intensive, especially if there are many products.
Well, there are many ways to handle this situation, like separate product attributes and attaching those when actually product converts to the cart however using a prototype pattern is also one approach where instead of creating mulyiple products onle prototype products are created and those are used to create actual further instances.

Prototype pattern solves the problem of cloning an object it provides an efficient way to create new objects by cloning existing ones, thereby minimizing the use of system resources and improving performance. It is particularly useful in situations where we need to create many objects with similar or identical properties, and where creating each object from scratch would be time-consuming or resource-intensive.

design problem scenario

Problem Statement :

Let's say we have an online store that sells customized t-shirts. Customers can choose the color, size, and design of the t-shirt.
1. To avoid creating new instances for all the variants we are thinking to provide a
way for the backend to use a base variant and then customize that base Tshirt
and then add that to the cart.
2. customer can also choose OLD orders, and choose Tshirt from that order as a
base variant, modify it and add to the cart.

public class TShirt implements Cloneable {
    private String color;
    private String size;
    private String design;
    private double price;

    public TShirt(String color, String size, String design, double price) {
        this.color = color;
        this.size = size;
        this.design = design;
        this.price = price;
    }

    public TShirt clone() throws CloneNotSupportedException {
        return (TShirt) super.clone();
    }

    //getters and setters
}

// cart
import java.util.ArrayList;
import java.util.List;

public class Cart {

    private List<TShirt> items;

    public Cart() {
        this.items = new ArrayList<>();
    }

    public void addItem(TShirt item) {
        items.add(item);
    }

    public void removeItem(TShirt item) {
        items.remove(item);
    }

    public double getTotal() {
        double total = 0.0;
        for (TShirt item : items) {
            total += item.getPrice();
        }
        return total;
    }

    public void printItems() {
        for (TShirt item : items) {
            System.out.println(item.getDesign() + " t-shirt, " + item.getSize() + ", " + item.getColor() + ": $" + item.getPrice());
        }
    }
    // other methods as needed
}

// order for second no point, getTshirt method is important. 
public class Order { 
    private TShirt tShirt;
    private int quantity;

    public Order(TShirt tShirt, int quantity) {
        this.tShirt = tShirt;
        this.quantity = quantity;
    }

    public TShirt getTShirt() throws CloneNotSupportedException {
        return tShirt.clone();
    }

    public int getQuantity() {
        return quantity;
    }
}

// 
public class Main {
    public static void main(String[] args) {
        // Create a prototype t-shirt our base default variant
        TShirt prototype = new TShirt("red", "M", "logo", 24.99);

        // Create an order for 3 t-shirts
        //Order order = new Order(prototype, 3);

        // Get a clone of the prototype t-shirt
        try {

            TShirt tShirt = prototype.clone();
            // Customize the t-shirt
            tShirt.setColor("blue");
            tShirt.setSize("L");
            tShirt.setDesign("text");
            tShirt.setPrice(29.99);

            // Add the customized t-shirt to the cart
            Cart cart = new Cart();
            cart.addItem(tShirt);
            System.out.println("\nPrinting items in the cart ");
            cart.printItems();


            // cloning the product from the OLD order, modify it and then modified product to the cart
            // Create a prototype t-shirt our base default variant  which belongs to the old order
            TShirt oldOrderTSHIRT = new TShirt("black", "L", "Name", 26.99);

            // Create an order for 3 t-shirts
            Order order = new Order(oldOrderTSHIRT, 3);

            // in the getShirt , its providing the cloned object .
            TShirt tShirt2 = order.getTShirt();
            // Customize the t-shirt
            tShirt2.setColor("blue");
            tShirt2.setSize("L");
            tShirt2.setDesign("Name");
            tShirt2.setPrice(26.99);

            // adding Tshirt to the cart
            cart.addItem(tShirt2);
            System.out.println("\nPrinting items in the cart ");
            cart.printItems();

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

    }
}

########## OUTPUT #############
Printing items in the cart 
text t-shirt, L, blue: $29.99

Printing items in the cart 
text t-shirt, L, blue: $29.99
Name t-shirt, L, blue: $26.99
###############################

complete github code [github-link]

Structure: prototype pattern

Client code, context, and strategies are the main components in the strategy pattern structure.
class and sequence diagram , source(wikipedia)

class and sequence diagram , source(wikipedia)

In the above diagram,
In the above class diagram the Client class refers to the Prototype interface for cloning a Product. The Product1 class implements the Prototype interface by creating a copy of itself.
The sequence diagram shows the run-time interactions: The Client object calls clone() on a prototype:Product1 object, which creates and returns a copy of itself.

In our example,
Client is our Main class which has reference to the order, when getProduct is done on Order it returns the cloned object.
Same goes with the prototype object created object is cloned manually and then its modified.

Applicability

1) The Prototype pattern is commonly used in situations where many objects with similar properties need to be created quickly and efficiently. This includes scenarios such as creating product catalogs, generating reports, and creating user profiles in a social network.

2) The Prototype pattern can also be used in situations where object creation is expensive or time-consuming, and where copying an existing object is faster and more efficient. This includes scenarios such as creating large and complex data structures.

Pros

  1. The Prototype pattern allows you to create new objects by cloning existing ones, which reduces the amount of time and resources required to create new objects.

  2. The Prototype pattern promotes code reusability by allowing you to define a set of prototypes that can be used to create new objects with similar properties.

  3. The Prototype pattern allows you to dynamically add or remove properties from objects at runtime, making it easier to modify existing objects without having to recreate them from scratch.

Cons

  1. The Prototype pattern can be complex to implement, especially when dealing with complex data structures and object graphs.

  2. The Prototype pattern can make it more difficult to enforce data integrity and maintain data consistency across objects, especially when dealing with shared or mutable data.

Famous usage of the prototype pattern in the java and spring framework

  1. java.lang.Object.clone(): The Object class in Java provides a clone() method that can be used to create a copy of an object. This method uses the Prototype pattern to create a new object by copying an existing one.
  1. Spring Framework's Prototype scope: In Spring, beans can be scoped to different levels, such as singleton, prototype, and request. The prototype scope is based on the Prototype pattern and allows you to define a bean as a prototype, which means that a new instance of the bean will be created each time it is requested. Spring Framework's AbstractBeanFactory.createBean() method: In Spring, the AbstractBeanFactory class provides a createBean() method that is used to create new instances of beans. When creating a new instance, the method first checks if the bean is scoped as a prototype. If it is, the method creates a new instance of the bean by cloning an existing prototype instance.

Relation with other patterns

Factory Method pattern: The Factory Method pattern and the Prototype pattern are both creational patterns that deal with the creation of objects. However, while the Factory Method pattern creates new objects by calling a factory method, the Prototype pattern creates new objects by cloning an existing prototype.
Factory method is based on Inheritance however prototype is not.

Decorator pattern: The Decorator pattern and the Prototype pattern are both related to the concept of object composition. However, while the Decorator pattern adds new behavior to an object by wrapping it with a decorator object, the Prototype pattern creates new objects by cloning an existing prototype.

Builder pattern: The Builder pattern and the Prototype pattern are both creational patterns that deal with the creation of objects. However, while the Builder pattern separates the construction of a complex object from its representation and allows the same construction process to create different representations, the Prototype pattern creates new objects by cloning an existing prototype.