Prototype pattern


Prototype Pattern is used to create repeated objects while ensuring performance. This type of design pattern is a creational pattern, which provides an optimal way to create objects.

This mode implements a prototype interface, which is used to create a clone of the current object. This mode is used when the cost of directly creating objects is relatively high. For example, an object needs to be created after an expensive database operation. We can cache the object, return a clone of it on the next request, and update the database when needed to reduce database calls.

Introduction

Intent: Use prototype instances to specify the types of objects to be created, and create new objects by copying these prototypes.

Main solution: Create and delete prototypes during runtime.

When to use: 1. When a system should be created, structured and represented independently of its products. 2. When the class to be instantiated is specified at run time, for example, through dynamic loading. 3. In order to avoid creating a factory class hierarchy parallel to the product class hierarchy. 4. When an instance of a class can only have one of several different state combinations. It may be more convenient to create a corresponding number of prototypes and clone them than to manually instantiate the class with the appropriate state each time.

How to solve: Use an existing prototype object to quickly generate an instance that is the same as the prototype object.

Key code: 1. To implement the cloning operation, inherit Cloneable in JAVA and rewrite clone(). In .NET, you can use the MemberwiseClone() method of the Object class to realize the shallow clone of the object. Copy or implement deep copy through serialization. 2. The prototype pattern is also used to isolate the coupling relationship between users of class objects and specific types (volatile classes). It also requires these "volatile classes" to have stable interfaces.

Application examples: 1. Cell division. 2. Object clone() method in JAVA.

Advantages: 1. Improved performance. 2. Escape the constraints of the constructor.

Disadvantages: 1. Equipping the clone method requires comprehensive consideration of the functions of the class. This is not difficult for new classes, but it is not necessarily easy for existing classes, especially when A class reference does not support serialized indirect objects, or when the reference contains a cyclic structure. 2. The Cloneable interface must be implemented. 3. Escape the constraints of the constructor.

Usage scenarios: 1. Resource optimization scenarios. 2. Class initialization requires digesting a lot of resources, including data, hardware resources, etc. 3. Scenarios with performance and security requirements. 4. If generating an object through new requires very cumbersome data preparation or access rights, you can use the prototype mode. 5. Scenarios with multiple modifiers of an object. 6. When an object needs to be accessed by other objects, and each caller may need to modify its value, you can consider using the prototype mode to copy multiple objects for use by the caller. 7. In actual projects, the prototype pattern rarely appears alone. It usually appears together with the factory method pattern. An object is created through the clone method, and then provided to the caller by the factory method. The prototype pattern has been integrated with Java and can be used by everyone.

Note: Unlike constructing a new object by instantiating a class, the prototype pattern generates a new object by copying an existing object. Shallow copy implements Cloneable, rewrite, and deep copy implements Serializable to read the binary stream.

Implementation

We will create an abstract class Shape and an entity class that extends the Shape class. The next step is to define the class ShapeCache that stores shape objects in a Hashtable and returns clones of them on request.

PrototypPatternDemo, our demo class uses the ShapeCache class to obtain the Shape object.

原型模式的 UML 图

Step 1

Create an abstract class that implements the Clonable interface.

Shape.java

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;
   }
}

Step 2

Create an entity class that extends the above abstract class.

Rectangle.java

public class Rectangle extends Shape {

   public Rectangle(){
     type = "Rectangle";
   }

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

Square.java

public class Square extends Shape {

   public Square(){
     type = "Square";
   }

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

Circle.java

public class Circle extends Shape {

   public Circle(){
     type = "Circle";
   }

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

Step 3

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

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();
   }

   // 对每种形状都运行数据库查询,并创建该形状
   // shapeMap.put(shapeKey, shape);
   // 例如,我们要添加三种形状
   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);
   }
}

Step 4

PrototypePatternDemo Use the ShapeCache class to get the A clone of the shape in Hashtable.

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());		
   }
}

Step 5

Verify the output.

Shape : Circle
Shape : Square
Shape : Rectangle