Book definition: "Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype."
Simple definition: "Create new objects by copying or cloning the existing object."
When to use Prototype Design Pattern?
When the object creation is a costly operation.
Example: An Object is to be created after reading data from a database. Reading data from the database is a costly operation.
Sample Implementation:
It consists of a base class "Shape" and two derived classes called "Square" and "Rectangle".
We are trying to maintain a map that would cache the "Square" and "Rectangle" object.
If a new request is to create a new "Square" or "Rectangle" object then it will check the cache map. If it exists then we would just clone the existing object and we will do the required operations on that.
Step 1: Create a base class "Shape" which implements "Cloneable" Interface
public class Shape implements Cloneable
{
protected String shapeName;
protected String description;
protected double area;
public Shape(String shapeName)
{
this.shapeName = shapeName;
this.description = "This class is about " + shapeName;
}
public String getShapeName()
{
return this.shapeName;
}
public String getDescription()
{
return this.description;
}
@Override
public Object clone() throws CloneNotSupportedException
{
Shape clonedShape = new Shape(this.shapeName);
return clonedShape;
}
}
Step 2: Create classes "Square" and "Rectangle" which extends "Shape" class
public class Square extends Shape implements Cloneable
{
private double dim;
public Square()
{
super("Square");
}
public void setDimension(double dim)
{
this.dim = dim;
}
public double getArea()
{
return dim * dim;
}
@Override
public Object clone() throws CloneNotSupportedException
{
Square clonedSquare = new Square();
return clonedSquare;
}
}
public class Rectangle extends Shape implements Cloneable
{
private double length;
private double breadth;
public Rectangle()
{
super("Rectangle");
}
public void setLength(int length)
{
this.length = length;
}
public void setBreadth(int breadth)
{
this.breadth = breadth;
}
public double getArea()
{
return length * breadth;
}
@Override
public Object clone() throws CloneNotSupportedException
{
Rectangle clonedRectangle = new Rectangle();
return clonedRectangle;
}
}
Step 3: Create a class "PrototypeMain" that has Cache.
public class PrototypeMain
{
public static Map shapeCache = new HashMap<>();
private static Shape getBaseShape(String shapeType) throws CloneNotSupportedException
{
if (shapeType.equalsIgnoreCase("square"))
{
if (shapeCache.containsKey("square"))
{
Shape square = (Shape) shapeCache.get("square").clone();
return square;
}
else
{
Shape square = new Square();
shapeCache.put(shapeType, square);
return square;
}
}
else if (shapeType.equalsIgnoreCase("rectangle"))
{
if (shapeCache.containsKey("rectangle"))
{
Shape rectangle = (Shape) shapeCache.get("rectangle").clone();
return rectangle;
}
else
{
Rectangle rectangle = new Rectangle();
shapeCache.put(shapeType, rectangle);
return rectangle;
}
}
return null;
}
public static void main(String[] args) throws CloneNotSupportedException
{
Square square1 = (Square) getBaseShape("square");
Square square2 = (Square) getBaseShape("square");
square1.setDimension(5);
System.out.println("Square 1 area is " + square1.getArea());
square2.setDimension(10);
System.out.println("Square 2 area is " + square2.getArea());
Rectangle rectangle1 = (Rectangle) getBaseShape("rectangle");
Rectangle rectangle2 = (Rectangle) getBaseShape("rectangle");
rectangle1.setLength(2);
rectangle1.setBreadth(1);
System.out.println("Rectangle 1 area is " + rectangle1.getArea());
rectangle2.setLength(4);
rectangle2.setBreadth(2);
System.out.println("Rectangle 2 area is " + rectangle2.getArea());
}
}
For more details, watch out my "Prototype Design Pattern" tutorial video