Prototype Design Pattern

Prototype Pattern – Definition

The Prototype Design Pattern is a creational design pattern which is defined as follows: Specify the kind of objects to create using a prototypical instance and create new objects by copying this prototype.

In this post we will break up this definition and learn what this pattern means and how to use it.

Problems in creating a copy of an object

Say you have an object and want to copy it to create a new object. This is not easy as there are a lot of problems. 

  1. An object can be very complex. It is difficult to copy an object per se and create an exact copy of a random object. Even for a finite set of objects, an object has internal states which is not exposed to the public.
  2. An object has references to other objects. When copying should we in turn copy them too? (Shallow vs deep copy).
  3. At last, (and probably the most important) we may be dealing with the base type or an interface. The actual runtime type of the object will not be known.

Thus, it is evident that a simple or a direct approach does not exist. We cannot copy an arbitrary object from the outside. The object must give some provision to achieve this.

Building components object hierarchy

Let us look at an example object hierarchy. Imagine a game application like SimCity which involves building a city. Let us deal with building a house. We have a base class called Component and let BuildingComponent be its subclass. Let us have Door and Wall as two concrete BuildingComponents. 

In a real game, there will be hundreds of such classes. We need a way to create/copy/clone any BuildingComponent object at runtime. 

If we use a builder class for each of the concrete classes (similar to the Factory Method Pattern) we will end up with something like shown below. The builder class has the logic encapsulated within it on how to build an object.

Class Hierarchy - Prototype Pattern
Class Hierarchy – Prototype Pattern

The problem with this is, now we have two class hierarchies – one for the domain classes (on the right) and a similar hierarchy of builder classes. Clearly, this is not scalable as we add new objects in the future. The number of classes will be increasing.

The Prototype Pattern

When we use the Prototype Design Pattern, we would delegate the copying or cloning process to the actual object itself. Each of the objects should support this cloning operation. Such an object is called a prototype. Once we have a prototype, creating or building a new object is to simply call the clone method on the object.

Then we can get rid of the builder hierarchy of classes and have just one common/generic builder class. The builder class will be composed or parameterised with a prototype. The type of object the builder builds then depends on what prototype we compose it with. Thus, we can change the runtime behaviour of the builder to create different objects by just changing the prototype. 

The client code (builder) is not coupled to any of the classes. In-fact, it does not even need to know the concrete type of objects it is creating – instead it can just work using the base class or interface (BuildingComponent in our example).

Applying the Prototype pattern

Let us add an abstract clone method to the base Component class.

public abstract class Component {
    public abstract Component clone();
}

Cloning a BuildingComponent

A BuildingComponent has length, width, and height fields. It has a constructor accepting these values as parameters, and there are getters and setters.

The clone method creates and returns a new BuildingComponent object. It passes the current object’s instance variable values to the constructor.

public class BuildingComponent extends Component {
    private double length;
    private double width;
    private double height;

    public BuildingComponent(double length, double width, double height) {
        this.length = length;
        this.width = width;
        this.height = height;
    }

    public double getLength() {
        return length;
    }

    public double getWidth() {
        return width;
    }

    public double getHeight() {
        return height;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    public void setHeight(double height) {
        this.height = height;
    }

    @Override
    public BuildingComponent clone() {
        return new BuildingComponent(length, width, height);
    }
}

There is another way to implement the clone. We can create a constructor that takes in an object of the same type (like a copy constructor). It can assign the fields from the passed object.

The clone method simply passes this as the argument to the constructor, which takes care of copying the fields as said above.

Both these approaches achieve the same result.

public class BuildingComponent extends Component {
    private double length;
    private double width;
    private double height;

    protected BuildingComponent(BuildingComponent buildingComponent) {
        this.length = buildingComponent.length;
        this.width = buildingComponent.width;
        this.height = buildingComponent.height;
    }
    //rest of code

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

Making other objects as prototype

Similarly, let us implement the Door and the Wall classes. A Door has the material instance field and the Wall class has the color of the wall as the field.

The clone method calls the copy constructor in the same class, which first calls the super constructor. That will initialize the length, width, and the height fields in the superclass. Once that call returns, it will complete the initialization of the other instance variables in the class.

public class Door extends BuildingComponent {
    private String material;

    public Door(double length, double width, double height, String material) {
        super(length, width, height);
        this.material = material;
    }
    private Door(Door door) {
        super(door);
        this.material = door.material;
    }

    @Override
    public BuildingComponent clone() {
        return new Door(this);
    }
}
import java.awt.Color;

public class Wall extends BuildingComponent {
    private Color wallColor;

    public Wall(double length, double width, double height, Color wallColor) {
        super(length, width, height);
        this.wallColor = wallColor;
    }
    private Wall(Wall wall) {
        super(wall);
        this.wallColor = wall.wallColor;

    }

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

The BuildingComponentBuilder

The BuildingComponentBuilder has a reference to a BuildingComponent. Note that all BuildingComponent objects are prototypes since they support the clone operation and enables anyone to create a copy of them.

public class BuildingComponentBuilder {
    private BuildingComponent buildingComponent;

    public BuildingComponentBuilder(BuildingComponent buildingComponent) {
        this.buildingComponent = buildingComponent;
    }

    public BuildingComponent buildNewComponent() {
        return buildingComponent.clone();
    }
}

The buildNewComponent method calls the clone method on the prototype reference it is composed with. This builder has no knowledge on the actual BuildingComponent class (It could be a Wall, Door or anything else). 

Prototype pattern – Test run

The below code creates a Door object and passes it (the prototype) to the Builder class. 

Door door = new Door(10, 10, 10, "Wood");
BuildingComponentBuilder buildingComponentBuilder = new BuildingComponentBuilder(door);
System.out.println("Original door is " + door);

Prints,

Original door is com.javadevcentral.designpatterns.prototype.model.Door@27bc2616

Note: The exact toString output will change from run to run.

If we call createNewComponent, each time it will create new Door objects. Let us call it twice.
System.out.println(buildingComponentBuilder.buildNewComponent());
System.out.println(buildingComponentBuilder.buildNewComponent());
com.javadevcentral.designpatterns.prototype.model.Door@3941a79c
com.javadevcentral.designpatterns.prototype.model.Door@506e1b77

We can see the (Hex value of the) hashCode is different in the two outputs and hence they are different objects. But each of these created objects will have the same value for the instance variables as the original prototype (length = width = height = 10 and material = Wood).

Once we create an object from a prototype, we can change few of its values to create an object with different properties.
BuildingComponent newDoor = buildingComponentBuilder.buildNewComponent();
System.out.println(newDoor.getHeight()); //10.0
newDoor.setHeight(20);
System.out.println(newDoor.getHeight()); //20.0

The newly built BuildingComponent object (Door) had a height of 10. But we changed it to 20 to create a new door of length 20. This way of creating new objects using prototype simplifies the creation of objects. We do not want to copy the individual fields, and they would be taken care by the object (prototype) itself.

Similarly, a wall component can be built and cloned as:

Wall wall = new Wall(10, 10, 20, Color.BLUE);
buildingComponentBuilder = new BuildingComponentBuilder(wall);
System.out.println("Original wall is " + wall);
System.out.println(buildingComponentBuilder.buildNewComponent());
Original wall is com.javadevcentral.designpatterns.prototype.model.Wall@1b2c6ec2
com.javadevcentral.designpatterns.prototype.model.Wall@4edde6e5

Advantages of the Prototype Design Pattern

  • Reduces the number of classes. We do not need to have a builder class for each object. Thus, it avoids having a separate hierarchy of Builder/Creator classes (if we had gone with an approach like the Factory Method approach)
  • The Builder is not coupled to any of the objects it copies. This is because the copying operation is performed by the objects themselves.
  • We can change the type of objects we create by changing the prototypical instance we use.
  • Can create different objects easily. Simply clone and change the properties needed.
    • Also, to create a new object, the client need not have access to the object’s constructor or the builder method. In other words, the client has has zero dependency on the object and its fields.

Structure

Prototype Pattern Class Diagram
Prototype Pattern Class Diagram

Participants

Prototype

  • An interface that has a method for cloning itself.
ConcretePrototype
  • Implements the Prototype interface.
Client
  • Calls the clone method on the prototype to create a new object.

Conclusion

This brings us to the end of this post. We learnt the prototype design pattern. We learnt how to make an object cloneable, the advantages of the Prototype pattern, and the ease with which it enables us to create new objects from existing prototypes.

References

  • Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.
  • SourceMaking

Leave a Reply