Design mode [5] - prototype mode

Posted by talkster5 on Sat, 11 Dec 2021 03:23:22 +0100

Start with a picture and write the rest

Design pattern article collection: http://aphysia.cn/categories/...

preface

Students who have come into contact with Spring or Springboot may know that beans are singleton by default, that is, they share the same object globally. Different objects will not be used because of different requests. We will not discuss singleton here. We have discussed the benefits and various implementations of singleton mode earlier. If you are interested, you can learn about it: http://aphysia.cn/archives/de... . In addition to the singleton, Spring can also set other scopes, that is, scope="prototype", which is the prototype mode. Every time a request comes, a new object will be created, which is created according to the prototype instance.

Definition of prototype pattern

Prototype mode is also a kind of creation mode. It refers to specifying the type of created objects with prototype instances, and creating new objects by copying these prototypes. In short, it is copying. Generally applicable to:

  • The instance is complex, the cost of complete creation is high, and direct replication is simple
  • Constructors are complex, and many unnecessary objects may be created

    advantage:

  • Hides the details of creating an instance
  • Creating objects is more efficient
  • If an object has a large number of the same attributes and only a few need to be specialized, you can directly copy the objects in the prototype mode and modify them to achieve the goal.

Implementation of prototype pattern

Generally speaking, the prototype pattern is used to copy objects, so the copied objects must have prototype classes, that is, prototype. Prototype needs to implement the clonable interface, which can be copied. Then rewrite the clone() method, and you can quickly obtain prototype objects according to different types.

Let's first define a prototype class Fruit:

public abstract class Fruit implements Cloneable{
    String name;
    float price;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }

    @Override
    public String toString() {
        return "Fruit{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

As well as the entity classes apple, pear and watermelons that extend the Fruit class:

public class Apple extends Fruit{
    public Apple(float price){
        name = "Apple";
        this.price = price;
    }
}
public class Pear extends Fruit{
    public Pear(float price){
        name = "snow pear";
        this.price = price;
    }
}
public class Watermelon extends Fruit{
    public Watermelon(float price){
        name = "watermelon";
        this.price = price;
    }
}

Create a cache class to get different fruit classes. Each time, take it out according to different types, copy it once and return it:

public class FruitCache {
    private static ConcurrentHashMap<String,Fruit> fruitMap =
            new ConcurrentHashMap<String,Fruit>();
    static {
        Apple apple = new Apple(10);
        fruitMap.put(apple.getName(),apple);

        Pear pear = new Pear(8);
        fruitMap.put(pear.getName(),pear);

        Watermelon watermelon = new Watermelon(5);
        fruitMap.put(watermelon.getName(),watermelon);
    }

    public static Fruit getFruit(String name){
        Fruit fruit = fruitMap.get(name);
        return (Fruit)fruit.clone();
    }
}

Test to obtain different fruits and compare the same type obtained twice. It can be found that the same type obtained twice is not the same object:

public class Test {
    public static void main(String[] args) {
        Fruit apple = FruitCache.getFruit("Apple");
        System.out.println(apple);

        Fruit pear = FruitCache.getFruit("snow pear");
        System.out.println(pear);

        Fruit watermelon = FruitCache.getFruit("watermelon");
        System.out.println(watermelon);

        Fruit apple1 = FruitCache.getFruit("Apple");
        System.out.println("Is it the same object" + apple.equals(apple1));
    }
}

The results are as follows:

Fruit{name='Apple', price=10.0}
Fruit{name='snow pear', price=8.0}
Fruit{name='watermelon', price=5.0}
false

Test again to see if the name attribute is the same object:

public class Test {
    public static void main(String[] args) {
        Fruit apple = FruitCache.getFruit("Apple");
        System.out.println(apple);

        Fruit apple1 = FruitCache.getFruit("Apple");
        System.out.println(apple1);
        System.out.println("Same object:" + apple.equals(apple1));
        System.out.println("Whether it is the same string object:" + apple.name.equals(apple1.name));
    }
}

The results are as follows. The string in the string does use the same object:

Fruit{name='Apple', price=10.0}
Fruit{name='Apple', price=10.0}
Same object: false
 Whether it is the same string object: true

Why? Because the clone() used above is a shallow copy!!! However, the string is immutable in Java. If it is modified, the original string will not be modified. Due to the existence of this attribute, it is similar to deep copy. If the attribute is another user-defined object, it should be noted that shallow copy will not really copy the object, but only copy a reference.

Here I have to introduce the difference between shallow copy and deep copy:

  • Shallow copy: there is no real copy of data, just a pointer to the data memory address
  • Deep copy: it not only creates a new pointer, but also copies a copy of data memory

If we use Fruit apple = apple1, we only copy the reference of the object, which is essentially the same object. Although the objects are different, the copy of Apple attributes still belongs to the same reference, and the address is the same. They share the original attribute object name.

How to make a deep copy? Generally, there are the following schemes:

  • Direct the new object, which doesn't need to be considered
  • Serialization and deserialization: after serialization and then deserialization, you can get a new object. Note that you must implement the Serializable interface.
  • Override the clone() method of the object yourself

Serialization for deep copy

The serialization implementation code is as follows:

Create a Student class and a School class:

import java.io.Serializable;

public class Student implements Serializable {
    String name;

    School school;

    public Student(String name, School school) {
        this.name = name;
        this.school = school;
    }
}
import java.io.Serializable;

public class School implements Serializable {
    String name;

    public School(String name) {
        this.name = name;
    }
}

Serialize copied classes:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class CloneUtil {
    public static <T extends Serializable> T clone(T obj) {
        T result = null;
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(obj);
            objectOutputStream.close();

            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            // Returns the generated new object
            result = (T) objectInputStream.readObject();
            objectInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

Test class:

public class Test {
    public static void main(String[] args) {
        School school = new School("Oriental primary school");
        Student student =new Student("Xiao Ming",school);

        Student student1= CloneUtil.clone(student);
        System.out.println(student.equals(student1));
        System.out.println(student.school.equals(student1.school));
    }
}

The above results are false, indicating that the same object is not the same object, and a deep copy has occurred.

clone enables deep copy

Both the previous Student and School implement the clonable interface, and then override the clone() method:

public class Student implements Cloneable {
    String name;

    School school;

    public Student(String name, School school) {
        this.name = name;
        this.school = school;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        student.school = (School) school.clone();
        return student;
    }
}
public class School implements Cloneable {
    String name;

    public School(String name) {
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Test class:

public class Test {
    public static void main(String[] args) throws Exception{
        School school = new School("Oriental primary school");
        Student student =new Student("Xiao Ming",school);

        Student student1= (Student) student.clone();
        System.out.println(student.equals(student1));
        System.out.println(student.school.equals(student1.school));
    }
}

The test results are also false, but also a deep copy has occurred.

summary

Prototype mode is applicable to scenes where creating objects requires many steps or resources. Among different objects, only some attributes need to be customized, and others are the same. Generally speaking, prototype mode will not exist alone, but will be used together with other modes. It is worth noting that the copy is divided into shallow copy and deep copy. If the data of shallow copy is modified, the data of different objects will be modified because they share metadata.

[about the author]:
Qin Huai, the official account of Qin Huai grocery store, is not in the right place for a long time. Personal writing direction: Java source code analysis, JDBC, Mybatis, Spring, redis, distributed, sword finger Offer, LeetCode, etc. I carefully write every article. I don't like the title party and fancy. I mostly write a series of articles. I can't guarantee that what I write is completely correct, but I guarantee that what I write has been practiced or searched for information. Please correct any omissions or mistakes.

Sword finger Offer all questions PDF

What did I write in 2020?

Open source programming notes

The official account of Qin Huai grocery store can receive the PDF solution of the Offer V1 version of the sword, the V2 version has added the title, is still in the update of the hum, and has added C++ solution to each topic.

Topics: Java Design Pattern