Prototype Pattern is a creational design pattern that lets you clone existing objects, rather than making new objects. This pattern is beneficial when the object creation process is complex, time-consuming, or requires a significant amount of resources.

Structure

The Prototype pattern consists of the following key components:

  • Prototype: is an interface or abstract class that defines the clone method and additional properties
  • Concrete Prototype: is a class that defines the logic for the clone method from Prototype, and can also handle additional cases
  • Client: uses objects returned by the clone method

When to use Prototype Pattern?

  • When you want to reduce the number of different subclasses having slightly different ways of initialization for their respective objects
  • When new object is slightly different from an already initialized object
  • When you want to get rid of repeated initialization code in favor of cloning pre-built prototypes

Implementation Example

The code below demonstrates the implementation of the Prototype design pattern in Java. The code showcases the usage of the Prototype pattern by creating an initial Burger object with a set of toppings and then cloning it to create new Burger objects with the same initial toppings. The cloned objects can be modified independently by adding additional toppings without affecting the original Burger object.

import java.util.*;

public class Solution {

    public static void main(String[] args) {
        
        // initial burger prototype
        VegBurger burger = new VegBurger();
        burger.addTopping("Buns");
        burger.addTopping("Tomato");
        burger.addTopping("Onions");
        burger.addTopping("Mustard Sauce");
        System.out.println("Original Burger - " + burger.toString());

        // cloned veg burger
        VegBurger clonVegBurger = (VegBurger) burger.clone();
        clonVegBurger.addTopping("Cheese Slice");
        clonVegBurger.addTopping("Hash Browns on Side");
        System.out.println("Cloned Veg Burger - " + clonVegBurger.toString());
    }
}

// the prototype class
abstract class Burger {

    public abstract Burger clone();
}

// concrete implementation of prototype
class VegBurger extends Burger {

    public List<String> toppings;

    public VegBurger() {

        toppings = new ArrayList<>();
    }

    public VegBurger(List<String> toppings) {

        this.toppings = toppings;
    }

    public void addTopping(String topping) {

        toppings.add(topping);
    }

    public Burger clone() {

        return new VegBurger(new ArrayList<>(toppings));
    }

    public String toString() {

        return "Burger Toppings: " + Arrays.toString(toppings.toArray());
    }
}

The output of the following program will be:

Original Burger - Burger Toppings: [Buns, Tomato, Onions, Mustard Sauce]
Cloned Veg Burger - Burger Toppings: [Buns, Tomato, Onions, Mustard Sauce, Cheese Slice, Hash Browns on Side]