Prototype Pattern

Prototype Pattern is used to create duplicate objects while ensuring performance. This type of design pattern belongs to creational patterns, and it provides one of the best ways to create objects.

This pattern implements a prototype interface, which is used to create a clone of the current object. This pattern is used when directly creating an object is relatively costly. For example, an object needs to be created after an expensive database operation. We can cache the object, return its clone on the next request, and update the database when needed, thereby reducing database calls.

summary

Prototype Pattern

intent: Use prototype instances to specify the kinds of objects to create, and create new objects by copying these prototypes.

Mainly Solves: Dynamically create and delete prototypes at runtime.

When to Use

  • The system should be independent of the creation, composition, and representation of products.
  • The class to instantiate needs to be specified at runtime, for example through dynamic loading.
  • Avoid creating a factory class hierarchy parallel to the product class hierarchy.
  • Instances of a class can only have a few different state combinations; cloning a prototype is more convenient than manual instantiation.

How to Solve

Quickly generate an instance identical to the prototype object by using an existing prototype object.

Key code

  • Implement the clone operation:
    • In Java, implementCloneableInterface, overrideclone()method.
    • In .NET, useObjectClassMemberwiseClone()The method implements shallow copy, or deep copy via serialization.
  • Isolate the coupling between class object users and concrete types, requiring "volatile classes" to have stable interfaces.

Application example

  • Cell Division
  • In JavaObject.clone()Methods

Advantages

  • Performance Improvement
  • Avoid constructor constraints

Disadvantages

  • Equipping a class with clone methods requires comprehensive consideration of the class's functionality, which may be difficult for existing classes, especially when dealing with indirect objects that do not support serialization or references containing circular structures.
  • Must ImplementCloneableInterface.

Use Case

  • Resource Optimization
  • When class initialization consumes a large amount of resources (such as data, hardware resources).
  • Scenarios with high performance and security requirements
  • ThroughnewWhen creating an object requires complex data preparation or access permissions.
  • An object requires multiple modifiers
  • When an object needs to be provided to other objects for access and may be modified by various callers.
  • It is often used together with the factory method pattern, throughclonecreating objects, which are then provided to callers by the factory method.

Notes

Unlike directly instantiating a class to create a new object, the Prototype Pattern generates new objects by copying existing objects. Shallow copy is achieved by implementingCloneablewhile deep copy is achieved by implementingSerializablereading binary streams.

Structure

The prototype pattern includes the following main roles:

  • Prototype Interface: Defines an interface for cloning itself, usually including aclone()method.

  • Concrete Prototype: A concrete class that implements the prototype interface and is responsible for the actual cloning operation. This class needs to implement theclone()method, usually using shallow copy or deep copy to copy itself.

  • Client: Use prototype instances to create new objects. The client calls the prototype object'sclone()method to create new objects instead of directly using constructors.

Implementation

We will create an abstract classShapeand extendsShapeConcrete classes of the class. The next step is to define the classShapeCache, this class stores shape objects in aHashtableand returns their clones when requested.

PrototypePatternDemoClass usageShapeCacheclass to getShapeObject.

原型模式的 UML 图

Step 1

Create an abstract class thatCloneableimplements the 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 entity classes that extend 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 that retrieves entity classes from a database and stores them in aHashtableIn.

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(); } //Run a database query for each shape and create that shape // shapeMap.put(shapeKey, shape); //For example, we want to add three shapes 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

PrototypePatternDemoUsageShapeCacheclass to get the prototype stored inHashtablethe clone of the shape inside.

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

Run the program, output result:

Shape : Circle
Shape : Square
Shape : Rectangle
other extensions