Overview

The Prototype pattern is a design pattern that allows the creation of objects by cloning existing objects, reducing the cost of object creation. The Prototype pattern consists of the following elements:

  • Prototype Interface or Abstract Class: Defines the cloning method that declares the ability to clone an object.
  • ConcretePrototype Class: Implements the Prototype interface concretely and provides the cloning method to perform object cloning.
  • Client: Requests the cloning operation and creates new objects.
// Prototype Interface
public interface Prototype {
    Prototype clone();
}

// ConcretePrototype Class
public class ConcretePrototype implements Prototype {
    private String property;

    public ConcretePrototype(String property) {
        this.property = property;
    }

    public Prototype clone() {
        return new ConcretePrototype(this.property);
    }

    public void setProperty(String property) {
        this.property = property;
    }

    public String getProperty() {
        return property;
    }
}

// Client Class
public class Client {
    public static void main(String[] args) {
        // Create an original object
        ConcretePrototype prototype = new ConcretePrototype("Original");

        // Clone the object
        ConcretePrototype clonedObject = (ConcretePrototype) prototype.clone();

        // Change the property of the cloned object
        clonedObject.setProperty("Cloned");

        System.out.println("Original Object: " + prototype.getProperty());
        System.out.println("Cloned Object: " + clonedObject.getProperty());
    }
}

In the above example code, we define the Prototype interface and have the ConcretePrototype class implementing it. The ConcretePrototype class provides the clone() method to clone itself. In the Client class, we create an original object and then clone it to create a new object. We modify the property of the cloned object and print the results.

Advantages

  • Reduces the cost of object creation by cloning existing objects. It avoids expensive initialization operations.
  • Simplifies the object creation process. Instead of complex initialization, it clones the existing object and makes necessary modifications.

Considerations

  • Be careful about shallow copy and deep copy during the cloning process. Shallow copy only copies the references of the referred objects, so the original and cloned objects may still refer to the same objects. Deep copy creates new instances of the referred objects, ensuring that the original and cloned objects refer to independent objects.
  • When an object has complex state, ensure that all state is accurately cloned. Take care while implementing the cloning method.
  • The Prototype pattern may become challenging to handle structural changes in objects dynamically. If the object’s structure changes, you may need to modify all the cloning-related code.

Here’s an example of incorrectly implementing object cloning and not using the Prototype pattern correctly:

// Prototype Interface
public interface Prototype {
    Prototype clone();
}

// ConcretePrototype Class
public class ConcretePrototype implements Prototype {
    private int[] array;

    public ConcretePrototype(int[] array) {
        this.array = array;
    }

    public Prototype clone() {
        return new ConcretePrototype(this.array); // Incorrect cloning approach
    }

    public void setArrayValue(int index, int value) {
        array[index] = value;
    }

    public int[] getArray() {
        return array;
    }
}

// Client Class
public class Client {
    public static void main(String[] args) {
        // Create the original object
        int[] originalArray = {1, 2, 3};
        ConcretePrototype prototype = new ConcretePrototype(originalArray);

        // Clone the object
        ConcretePrototype clonedObject = (ConcretePrototype) prototype.clone();

        // Modify the array value of the cloned object
        clonedObject.setArrayValue(0

, 99);

        System.out.println("Original Array: " + Arrays.toString(prototype.getArray()));
        System.out.println("Cloned Array: " + Arrays.toString(clonedObject.getArray()));
    }
}

In the above example code, the clone() method in the ConcretePrototype class performs shallow copying of the array. As a result, the original and cloned objects end up referencing the same array object. Therefore, when modifying the array value of the cloned object, the array value of the original object also changes.

In such cases, the correct approach is to perform deep copying to ensure that the cloned object and the original object refer to independent array objects. For example, you can create a new array instance and copy the array elements to perform cloning correctly.

Patterns Used with the Prototype Pattern

  • Abstract Factory Pattern: The Abstract Factory pattern can be used to manage Prototype instances and create new objects based on them.
  • Builder Pattern: The Builder pattern can be used to clone Prototype objects and perform additional configuration operations.
  • Singleton Pattern: The Singleton pattern can be used to share Prototype instances and create new objects by cloning them when needed.