This is the fourth day of my participation in the August Text Challenge.More challenges in August

After the singleton, factory, and Builder patterns, today we’ll look at the last creative design pattern: the prototype pattern.

Principle and Application

When the system needs to create a large number of identical or similar objects, the “prototyping pattern” can be used to achieve this.

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 as needed to reduce database calls.

The core idea of the prototype pattern is to create a new object just like the object by copying the specified “prototype instance (object).” Simple to understand is “clone the specified object”.

case

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

The PrototypeDemo class uses the ShapeCache class to get Shape objects.

Create an abstract class that implements the Cloneable interface.

public abstract class Shape implements Cloneable {

    private String id;
    protected String type;

    abstract void draw(a);

    public String getType(a) {
        return type;
    }

    public String getId(a) {
        return id;
    }

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

    @Override
    protected Object clone(a) {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        returnclone; }}Copy the code

2. Create an entity class that extends the abstract class above.

public class Rectangle extends Shape{

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

    @Override
    public void draw(a) {
        System.out.println("Inside Rectangle::draw() method."); }}public class Square extends Shape{

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

    @Override
    public void draw(a) {
        System.out.println("Inside Rectangle::draw() method."); }}public class Circle extends Shape{

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

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

3. Create a class that retrieves entity classes from the database and stores them in a Hashtable.

public class ShapeCache {

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

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

    public static void loadCache(a) {
        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); }}Copy the code

4. PrototypeDemo uses the ShapeCache class to obtain data stored inHashtableIn the shape of the clone.

public class PrototypeDemo {
    public static void main(String[] args) {
        ShapeCache.loadCache();

        Shape clonedShape = 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()); }}Copy the code

Output:

Shape : Circle

Shape : Square

Shape : Rectangle

The above case from the rookie tutorial.

In fact, the concept of prototype pattern is easy to understand, mainly because of the way it is implemented object clone design to deep copy and shallow copy.

Deep copy and shallow copy

Shallow copy

A shallow copy is a bitwise copy of an object that creates a new object with an exact copy of the original object’s property values. If the property is of a primitive type, the value of the primitive type is copied. If the property is a memory address (reference type), the memory address is copied, so if one object changes the address, the other object will be affected. That is, the default copy constructor only copies objects shallowly (member by member), that is, only the object space is copied, not the resource.

Features:

  1. For member objects of primitive data types, the attribute value is assigned directly to the new object because the primitive data type is value passed. A copy of a base type in which one object changes the value without affecting the other.
  2. For reference types, such as arrays or class objects, because reference types are passed by reference, shallow copies simply assign memory addresses to member variables that point to the same memory space. If you change one, you have an effect on the other.

Deep copy

Deep copy: when copying a reference type member variable, it creates an independent memory space for the reference type data member, realizing the true content copy.

Features:

  1. For member objects of primitive data types, the attribute value is assigned directly to the new object because the primitive data type is value passed. A basic type of copy in which one object changes the value without affecting the other (as in a shallow copy).
  2. For reference types, such as arrays or class objects, deep copy creates a new object space and copies its contents, so they point to different memory Spaces. Changing one does not affect the other.
  3. For objects with multiple layers, each object needs to be implementedCloneablePay equal attention to writingclone()Then the serial layer copy of object is realized.
  4. Deep copy is slower and more expensive than shallow copy

How to implement deep copy:

  1. Recursively copy objects, reference objects of objects, and reference objects of reference objects… Until the object to be copied contains only basic data type data and no reference objects. Refactoring the previous code along these lines.

  2. The object is serialized and then deserialized into a new object. Example code is shown below:

Conclusion:

Shallow-copy sharing immutable objects is fine if the object to be copied is immutable, but for mutable objects, the shallow-copy object shares some data with the original object, and the risk of data modification becomes much more complicated. Shallow copy is recommended unless the database operation is very time consuming, but there is no good reason to use shallow copy for a small performance gain.