Prototype

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying the implemented prototype.

Concept

In general creating a new object from scratch is a costly operation. When multiple instances of same type has be created, it would be quick to create those instances by cloning or copying the existing prototype instance.

Note:

Consider the Object.clone() method as an example of prototype.

Analogy

Suppose we have a master copy of a document, and we need to add some more content to the document. We can make photocopy of the original document and add extra content to it with creating a new document from scratch. Because creating document from scratch is a tedious task and so photocopy comes to rescue.

Implementation In Java

In java, prototype pattern can be implemented using the Object.clone() method. And the cloneable class must implement the interface Cloneable

Other ways to create copy the object is using serialization mechanism or by creating your own copy constructor and use it.

There are two types of copy in java

Shallow Copy

A shallow copy creates a new object and then copies various field values from the original object to the new object. It is also known as a field-by-field copy. If the original object contains any references to other objects as fields, then the references of those objects are copied into the new object, (i.e., you do not create the copies of those objects).

Let’s take an example to understand the underlying mechanism.

Suppose there is an object J, it has reference to other object A, and object A has reference to object B as shown below

flowchart LR
    objectJ((object J))
    objectA((object B))
    objectC((object C))

    objectJ --> objectA --> objectC

Let’s say, shallow copy of object J is Object K. Now, the reference of A is copied into object K as depicted in below

flowchart LR
    objectJ((object J))
    objectK((object K))
    objectA((object B))
    objectC((object C))

    objectJ --> objectA --> objectC
    objectK --> objectA

Deep Copy

In a deep copy, the new object is totally separated from the original one. Any changes made in one object should not be reflected on the other one. To create a deep copy in Java, you may need to override the clone() method and then proceed. Also, a deep copy is expensive because you need to create additional objects.

So, from the shallow copy example, object K will have copy of data and with new references.

Class Diagram

Following is the Class diagram for prototype pattern implementation.

classDiagram

  class Cloneable {
    <<interface>>
  }

  class BasicCar {
    <<abstract>>
    +String modelName
    +double onRoadPrice
    +BasicCar()
    +setModelName(String setModelName)
    +setOnRoadPrice(double onRoadPrice)
    +clone() BasicCar
  }

  class Jaguar {
    +Jaguar()
    +clone() BasicCar
  }

  class Ford {
    +Ford()
    +clone() Ford
  }

  class Client {
    +process()
  }

  Client --> BasicCar
  Cloneable <|.. BasicCar: implements
  BasicCar <|-- Jaguar: extends
  BasicCar <|-- Ford: extends

Illustration

  • BasicCar is a prototype class, and it implements Cloneable marker interface.
  • Whichever class extends BasicCar should override clone() method to create a shallow copy of BasicCar.
  • Client uses the prototype class to create objects using clone() method.

Prototype Class Implementation

As the BasicCar implements Cloneable marker interface, it has to implement clone() method and method throws CloneNotSupportedException

BasicCar.java

abstract class BasicCar implements Cloneable {
    //  declare attributes
    public String modelName;
    public double onRoadPrice;

    //  constructor
    public BasicCar() {}

    //  set modelName
    public void setModelName(String modelName) {
        this.modelName = modelName;
    }

    //  set onRoadPrice
    public void setOnRoadPrice(double onRoadPrice) {
        this.onRoadPrice = onRoadPrice;
    }

    //  clone method
    public BasicCar clone() throws CloneNotSupportedException {
        return (BasicCar) super.clone();
    }
}

Sub-Classes Implementation

Subclasses which extends the BasicCar overrides clone() method.

Jaguar.java

class Jaguar extends BasicCar {

    // constructor
    public Jaguar() {
        super();
    }

    @Override //    override clone method
    public BasicCar clone() throws CloneNotSupportedException {
        return super.clone();
    }

    // class specific methods
    public void doSomething() {
        // do something
    }
}

Ford.java

class Ford extends BasicCar {

    // constructor
    public Ford() {
        super();
    }

    @Override //    override clone method
    public BasicCar clone() throws CloneNotSupportedException {
        return super.clone();
    }

    // class specific methods
    public void doSomething() {
        // do something
    }
}

Client Implementation

Clint creates BasicCar objects and clone the existing objects to create new objects.

Client.java

class Client {
    public void process() throws CloneNotSupportedException {

        // create a basic jaguar object
        BasicCar ePase = new Jaguar();
        ePase.setModelName("E-Pase");
        ePase.setOnRoadPrice(100_000);

        // create new clone jaguar object
        BasicCar fPase = ePase.clone();
        fPase.setModelName("F-Pase");
        fPase.setOnRoadPrice(200_000);

        // create a basic Ford Object
        BasicCar endeavour = new Ford();
        endeavour.setModelName("Endeavour");
        endeavour.setOnRoadPrice(50_000);

        // create new clone ford object
        BasicCar ecoSport = endeavour.clone();
        ecoSport.setModelName("Endeavour");
        ecoSport.setOnRoadPrice(50_000);

    }
}

Advantages

  • It is useful when the creation of the class is expensive and complicated process.
  • It helps to focus on key activities only leaving complex object creation.
  • Helps to include or discard products at realtime

Disadvantages

  • Every subclass that extends the prototype class needs to implement the clone() method or copying mechanism.
  • Sometimes creating a copy of the object is not simple if the objects does not support cloning or if there are circular references.
  • In Java, a class with clone() method needs to implement Cloneable interface, else it will throw CloneNotSupportedException.

When To Use

  • When the object creation is expensive and may have to create collection of same object types.
  • Avoid having too many classes that have few variants of state.
  • Avoid parallel hierarchy of factories for your product classes.
  • Dynamically load classes in the application
  • Achieve loose coupling between the classes

Known Uses

  • Certain languages have inbuild support for cloning the either through language features or frameworks.
  • Used mostly in CAD/CAM applications, games, etc.

Java

  • A simple example of prototype pattern used in java is
java.lang.Object.clone() 
  • Designs that use Composite Pattern and Decorator Pattern can benefit from Prototype pattern heavily.
  • Prototype pattern is unique among the other patterns as it needs an object to create new instances while other patterns rely on classes to create new instances.
  • Abstract Factory can be implemented by using Prototype Pattern instead of a Factory Method
  • Prototype Pattern requires initialization but does not require sub-typing while Factory Method Pattern and Abstract Factory Pattern requires sub-typing but does not need to initialize.

Learn other design patterns

Subscribe For More Content