What is the Prototype Design Pattern
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.
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
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?
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.