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 oneclone()
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 constructorinside constructors:
we call the
clone()
method for reference objects where necessaryother 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