Design Patterns - Prototype (in Java)

Design Patterns - Prototype (in Java)

You wanna create an exact copy?

Definition

The Prototype Design Pattern is a creational pattern that allows you to create new objects by copying an existing object, known as the prototype.

The copy could be shallow or deep, depending on our requirements.

Motivation

Let’s consider a nested objects scenario. A Car object HAS-A Engine object & Engine object HAS-A Specification object.

If you need to create an exact copy of your Car object, it will not be feasible to create an exact copy of the nested objects individually.

In that case, one of the biggest problems would be:

  • some classes could have private properties, which can’t be copied directly from outside

We have a better solution - Prototype Design Pattern.

Towards Prototype Pattern - Step by Step

  • We will introduce a Prototype interface with one clone() method

  • Each class will implement the clone() functionality based on requirement (shallow/deep)

  • Each class will have a constructor with its own object

Here is the Java code:

interface Prototype {
    Prototype clone();
}

class Specification implements Prototype {
    private String fuelType;
    private int horsePower;

    public Specification(String fuelType, int horsePower) {
        this.fuelType = fuelType;
        this.horsePower = horsePower;
    }

    private Specification(Specification spec) {
        this.fuelType = spec.fuelType;
        this.horsePower = spec.horsePower;
    }

    public void setFuelType(String fuelType) {
        this.fuelType = fuelType;
    }

    public String getFuelType() {
        return fuelType;
    }

    public void setHorsePower(int horsePower) {
        this.horsePower = horsePower;
    }

    public int getHorsePower() {
        return horsePower;
    }

    @Override
    public Specification clone() {
        return new Specification(this);
    }
}

class Engine implements Prototype {
    private String type;
    private Specification specification;

    public Engine(String type, Specification specification) {
        this.type = type;
        this.specification = specification;
    }

    private Engine(Engine engine) {
        this.type = engine.type;
        this.specification = engine.specification.clone();
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }

    public Specification getSpecification() {
        return specification;
    }

    public void setSpecification(Specification specification) {
        this.specification = specification;
    }

    @Override
    public Engine clone() {
        return new Engine(this);
    }
}

class Car implements Prototype {
    private String model;
    private Engine engine;

    public Car(String model, Engine engine) {
        this.model = model;
        this.engine = engine;
    }

    private Car(Car car) {
        this.model = car.model;
        this.engine = car.engine.clone();
    }

    public void setModel(String model) {
        this.model = model;
    }

    public String getModel() {
        return model;
    }

    public Engine getEngine() {
        return engine;
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    @Override
    public Car clone() {
        return new Car(this);
    }
}

class PrototypeDemo {
    public static void main(String[] args) {
        Specification specification = new Specification("Diesel", 200);
        Engine engine = new Engine("V8", specification);
        Car car = new Car("Toyota Supra", engine);

        // Clone the Car object
        Car clonedCar = car.clone();

        System.out.println("Car: " + (car == clonedCar));
        System.out.println("Engine: " + (car.getEngine() == clonedCar.getEngine()));
        System.out.println("Specification: " + (car.getEngine().getSpecification() == clonedCar.getEngine().getSpecification()));
    }
}

In the above code:

  • From clone() method, we call the private constructor

    • inside constructors:

      • we call the clone() method for reference objects where necessary

      • other than that, we just copied the values

      • e.g. for Engine

      •   private Engine(Engine engine) {
              this.type = engine.type; //copy the value
              // called clone() method for reference object
              this.specification = engine.specification.clone();
          }
        

I put a debug point and here is the output:

From the picture, it is clear, original and cloned reference objects are not the same.

Actually, in Java, we have a Clonable interface which is similar to the Prototype interface.

Benefits of Prototype Pattern

  • Makes it easy to create a deep copy of the complex nested object