Prototype design pattern in Java and Python Development 27.09.2016

Creational Design Patterns provide solution to instantiate an object in the best possible way for specific situations. Following design patterns come under this category.

Prototype pattern creates object based on an existing object through cloning.

Prototype pattern is used when the object creation is a costly affair and requires a lot of time and resources and you have a similar object already existing. So this pattern provides a mechanism to copy the original object to a new object and then modify it according to our needs.

For example, an object is to be created after a costly database operation. We can cache the object, returns its clone on next request and update the database as and when needed thus reducing database calls.

When to use:

  • when the classes to instantiate are specified at run-time, for example, by dynamic loading;
  • to avoid building a class hierarchy of factories that parallels the class hierarchy of products;
  • when instances of a class can have one of only a few different combinations of state. It may be more convenient to install a corresponding number of prototypes and clone them rather than instantiating the class manually, each time with the appropriate state.

The main advantages of prototype pattern are as follows:

  • It reduces the need of sub-classing.
  • It hides complexities of creating objects.
  • The clients can get new objects without knowing which type of object it will be.
  • It lets you add or remove objects at runtime.

We're going to create an abstract class Shape and concrete classes extending the Shape class. A class ShapeCache is defined as a next step which stores shape objects in a Hashtable and returns their clone when requested.

PrototypPatternDemo, our demo class will use ShapeCache class to get a Shape object.

dp_prototype.jpg

Java

Create an abstract class implementing Clonable interface.

public abstract class Shape implements Cloneable {
   private String id;
   protected String type;

   abstract void draw();

   public String getType(){
      return type;
   }

   public String getId() {
      return id;
   }

   public void setId(String id) {
      this.id = id;
   }

   public Object clone() {
      Object clone = null;

      try {
         clone = super.clone();

      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
      }

      return clone;
   }
}

Create concrete classes extending the above class.

// file Rectangle.java
public class Rectangle extends Shape {
   public Rectangle(){
     type = "Rectangle";
   }

   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

// file Square.java
public class Square extends Shape {
   public Square(){
     type = "Square";
   }

   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}

// file Circle.java
public class Circle extends Shape {
   public Circle(){
     type = "Circle";
   }

   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}

Create a class to get concrete classes from database and store them in a Hashtable.

// file ShapeCache.java

import java.util.Hashtable;

public class ShapeCache {

   private static Hashtable<String, Shape> shapeMap  = new Hashtable<String, Shape>();

   public static Shape getShape(String shapeId) {
      Shape cachedShape = shapeMap.get(shapeId);
      return (Shape) cachedShape.clone();
   }

   public static void loadCache() {
      Circle circle = new Circle();
      circle.setId("1");
      shapeMap.put(circle.getId(),circle);

      Square square = new Square();
      square.setId("2");
      shapeMap.put(square.getId(),square);

      Rectangle rectangle = new Rectangle();
      rectangle.setId("3");
      shapeMap.put(rectangle.getId(), rectangle);
   }
}

PrototypePatternDemo uses ShapeCache class to get clones of shapes stored in a Hashtable.

// file PrototypePatternDemo.java
public class PrototypePatternDemo {
   public static void main(String[] args) {
      ShapeCache.loadCache();

      Shape clonedShape = (Shape) ShapeCache.getShape("1");
      System.out.println("Shape : " + clonedShape.getType());       

      Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
      System.out.println("Shape : " + clonedShape2.getType());      

      Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
      System.out.println("Shape : " + clonedShape3.getType());      
   }
}

Python

import abc
import copy

class Shape(metaclass=abc.ABCMeta):
    def __init__(self):
        self.id = None
        self.type = None

    @abc.abstractmethod
    def draw(self):
        pass

    def get_type(self):
        return self.type

    def get_id(self):
        return self.id

    def set_id(self, sid):
        self.id = sid

    def clone(self):
        return copy.copy(self)


class Rectangle(Shape):
    def __init__(self):
        super().__init__()
        self.type = "Rectangle"

    def draw(self):
        print("Inside Rectangle::draw() method.")


class Square(Shape):
    def __init__(self):
        super().__init__()
        self.type = "Square"

    def draw(self):
        print("Inside Square::draw() method.")


class Circle(Shape):
    def __init__(self):
        super().__init__()
        self.type = "Circle"

    def draw(self):
        print("Inside Circle::draw() method.")


class ShapeCache:
    cache = {}

    @staticmethod
    def get_shape(sid):
        shape = ShapeCache.cache.get(sid, None)
        return shape.clone()

    @staticmethod
    def load():
        circle = Circle()
        circle.set_id("1")
        ShapeCache.cache[circle.get_id()] = circle

        square = Square()
        square.set_id("2")
        ShapeCache.cache[square.get_id()] = square

        rectangle = Rectangle()
        rectangle.set_id("3")
        ShapeCache.cache[rectangle.get_id()] = rectangle


if __name__ == '__main__':
    ShapeCache.load()

    circle = ShapeCache.get_shape("1")
    print(circle.get_type())

    square = ShapeCache.get_shape("2")
    print(square.get_type())

    rectangle = ShapeCache.get_shape("3")
    print(rectangle.get_type())