Prototype Design Pattern

What is the Prototype Design Pattern

The prototype design pattern is a method where a new instance of an object is obtained by asking a manager to return the clone of one.

In the earlier tutorials we saw how abstract factory pattern can be used to create a family of objects and builder pattern can be used to build objects in a step by step manner. In this tutorial we look at another creational pattern called the Prototype pattern. In this design pattern we pre-create an object or composite object and register it with a manager. Whenever a client needs that object it asks the manager, which returns a clone of the required object. We demonstrate the pattern using the following car builder pattern.

Example
We design a framework that can build Ford vehicles. The framework can build cars or SUVs. A car always comes with a 2 wheel drive, a basic maintenance plan and a partial insurance plan. An SUV comes with a four wheel drive, a comprehensive insurance plan and complete insurance. We Use a prototype manager to create the basic car and suv objects along with their maintenance and insurance objects. Each car or SUV model can then build upon this object to create its own variant

Before we build the prototype manager lets look at the objects

The FordVehicle class defines a Ford Vehicle. It has setters for Insurance, maintenance and Drive type. Ford Car and Ford SUV are examples of Ford Vehicles.

package com.studytrails.patterns.java.prototype;
public class FordVehicle
{
	Insurance insurance;
	Maintenance maintenance;
	DriveType driveType;
	public void setInsurance(Insurance insurance)
	{
		this.insurance = insurance;
	}
	public void setMaintenance(Maintenance maintenance)
	{
		this.maintenance = maintenance;
	}
	public void setDriveType(DriveType driveType)
	{
		this.driveType = driveType;
	}
	public FordVehicle clone() throws CloneNotSupportedException
	{
		FordVehicle vehicle = (FordVehicle) super.clone();
		vehicle.setInsurance(insurance.clone());
		vehicle.setMaintenance(maintenance.clone());
		vehicle.setDriveType(driveType.clone());
		return vehicle;
	}
}
				

public interface Insurance 
{
	public void setName(String name);
	public Insurance clone() throws CloneNotSupportedException;
}
public class PartialInsurance implements Insurance
{
	String name;
	public PartialInsurance(String name)
	{
		this.name = name;
	}
	public void setName(String name)
	{
		this.name = name;

	}
	public Insurance clone() throws CloneNotSupportedException
	{
		PartialInsurance insurance = (PartialInsurance) super.clone();
		insurance.setName(this.name);
		return insurance;
	}
}
public class CompleteInsurance implements Insurance
{
	String name;
	public CompleteInsurance(String name)
	{
		this.name = name;
	}
	public void setName(String name)
	{
		this.name = name;

	}
	public Insurance clone() throws CloneNotSupportedException
	{
		CompleteInsurance insurance = (CompleteInsurance) super.clone();
		insurance.setName(this.name);
		return insurance;
	}
}
				

public interface Maintenance extends Cloneable
{
	public void setName(String name);
	public Maintenance clone() throws CloneNotSupportedException;
}
public class BasicMaintenance implements Maintenance
{
	String name;
	public BasicMaintenance(String name)
	{
		this.name = name;
	}
	public void setName(String name)
	{
		this.name = name;
	}
	public Maintenance clone() throws CloneNotSupportedException
	{
		BasicMaintenance maintenance = (BasicMaintenance) super.clone();
		maintenance.setName(this.name);
		return maintenance;
	}
}
public class ComprehensiveMaintenance implements Maintenance
{
	String name;
	public ComprehensiveMaintenance(String name)
	{
		this.name = name;
	}
	public void setName(String name)
	{
		this.name = name;
	}
	public Maintenance clone() throws CloneNotSupportedException
	{
		ComprehensiveMaintenance maintenance = (ComprehensiveMaintenance) super.clone();
		maintenance.setName(this.name);
		return maintenance;
	}
}

				

public interface DriveType
{
	public DriveType clone() throws CloneNotSupportedException;
}
public class TwoWheelDrive implements DriveType
{
	public DriveType clone() throws CloneNotSupportedException
	{
		return (DriveType) super.clone();
	}
}
public class FourWheelDrive implements DriveType
{
	public DriveType clone() throws CloneNotSupportedException
	{
		return (DriveType) super.clone();
	}
}
				

Lets see how we would create different kinds of Ford Vehicles in a non design pattern world

public class ClientBasic
{
	public static void main(String[] args) throws CloneNotSupportedException
	{
		ClientBasic client = new ClientBasic();
		client.buildCar();
	}
	private void buildCar()
	{
		// cars
		FordVehicle fordFiesta = new FordVehicle();
		fordFiesta.setDriveType(new TwoWheelDrive());
		fordFiesta.setInsurance(new PartialInsurance("partialInsurance"));
		fordFiesta.setMaintenance(new BasicMaintenance("basicMaintenance"));

		FordVehicle fordMustang = new FordVehicle();
		fordMustang.setDriveType(new TwoWheelDrive());
		fordMustang.setInsurance(new PartialInsurance("partialInsurance"));
		fordMustang.setMaintenance(new BasicMaintenance("basicMaintenance"));

		// SUVs
		FordVehicle fordEscape = new FordVehicle();
		fordEscape.setDriveType(new FourWheelDrive());
		fordEscape.setInsurance(new CompleteInsurance("completeInsurance"));
		fordEscape.setMaintenance(new ComprehensiveMaintenance("comprehensiveMaintenance"));

		FordVehicle fordExplorer = new FordVehicle();
		fordExplorer.setDriveType(new FourWheelDrive());
		fordExplorer.setInsurance(new CompleteInsurance("completeInsurance"));
		fordExplorer.setMaintenance(new ComprehensiveMaintenance("comprehensiveMaintenance"));
	}
}

Lets see the problems with this approach

  • The client has to create multiple objects
  • The client needs to know the concrete implementation of each type. For example the client needs to know the implementation of each Insurance or Maintenance type
  • The client(s) cannot reuse logic. For example all clients have to know that cars have partial insurance and two wheel drive, and has to specify that each time.

we will now use the prototype pattern to modify the example. The example uses a prototype manager that is initialized with the maintenance, insurance and drive type objects. It also creates the basic car and SUV objects initialized with appropriate maintenance, insurance and drive type. The client can request a car and SUV object from this manager, which returns a
clone
of a car or an SUV.

Here’s a class diagram using the prototype pattern

Prototype Design Pattern

We have introduced a Prototype manager that the client uses to request vehicles. The prototype manager returns clones of the objects. Lets look at the classes.

Prototype Manager

public class PrototypeManager
{
	private Insurance partialInsurance;
	private Insurance completeInsurance;
	private Maintenance basicMaintenance;
	private Maintenance comprehensiveMaintenance;
	private DriveType twoWheelDrive;
	private DriveType fourWheelDrive;
	private Map<String , FordVehicle> fordRegistry = new HashMap<String , FordVehicle>();
	public PrototypeManager()
	{
		initialize();
		initializeVehicles();
	}
	private void initialize()
	{
		partialInsurance = new PartialInsurance("partialInsurance");
		completeInsurance = new CompleteInsurance("completeInsurance");
		basicMaintenance = new BasicMaintenance("basicMaintenance");
		comprehensiveMaintenance = new ComprehensiveMaintenance("comprehensiveMaintenance");
		twoWheelDrive = new TwoWheelDrive();
		fourWheelDrive = new FourWheelDrive();
	}
	private void initializeVehicles()
	{
		FordVehicle fordCar = new FordVehicle();
		fordCar.setDriveType(twoWheelDrive);
		fordCar.setInsurance(partialInsurance);
		fordCar.setMaintenance(basicMaintenance);
		fordRegistry.put("fordCar", fordCar);

		FordVehicle fordSUV = new FordVehicle();
		fordSUV.setDriveType(fourWheelDrive);
		fordSUV.setInsurance(completeInsurance);
		fordSUV.setMaintenance(comprehensiveMaintenance);
		fordRegistry.put("fordSUV", fordSUV);
	}
	public FordVehicle getVehicle(String key) throws CloneNotSupportedException
	{
		FordVehicle vehicle = fordRegistry.get(key);
		return vehicle.clone();
	}
}

Client

public class Client
{
	PrototypeManager manager;
	public static void main(String[] args) throws CloneNotSupportedException
	{
		Client client = new Client();
		client.buildCar();
	}
	public void buildCar() throws CloneNotSupportedException
	{
		manager = new PrototypeManager();
		FordVehicle fordFiesta = manager.getVehicle("fordCar");
		FordVehicle fordMustang = manager.getVehicle("fordCar");
		FordVehicle fordEscape = manager.getVehicle("fordSUV");
		FordVehicle fordExplorer = manager.getVehicle("fordSUV");
	}
}

What have we changed?

The prototype manager creates the Insurance, Maintenance and Drive Type objects. It also creates a base car and SUV object. When the client requests a car object, the prototype manager retrieves one from the repository and returns a clone.

The client can build upon the basic car and SUV objects. It does not have to know that all cars have two wheel drive or all SUVs have a comprehensive maintenance. The client does not have to know about the concrete implementations and codes only to references.

Unlike the Abstract factory, there is no profusion of classes here. A single prototype manager can take care of multiple product combinations.

What are the other features of Prototype pattern?

  • We can register other implementations with prototype manager. For example, we can register a basic truck implemenation with the prototype manager using this method
    public void addVehicle(String key, FordVehicle vehicle){
    	fordRegistry.put(key, vehicle);
    }
    		
  • If Ford decides to use Comprehensive maintenance for cars we only have to modify the prototype manager.
  • There are different ways to implement the prototype pattern. We could have defined a FordCar class that extends FordVehicle and then returned a clone from the FordCar. Either way the concept remains same.

This completes our example on prototype pattern. Please follow us on facebook or google to get new tutorials as we write them.

Leave a Comment