Java Design Pattern-Prototype Pattern

1 Introduction

In some systems, there are a lot of problems with the creation of the same or similar objects. If you use traditional constructors to create objects, it will be more complicated and time-consuming and resource-consuming. Using prototype mode to generate objects is very efficient, just like Monkey King plucking out monkey hair It's as simple as turning into a lot of Monkey Kings with a single blow.

2. The definition and characteristics of the prototype model

The definition of the prototype (Prototype) mode is as follows: use an already created instance as a prototype, and create a new object that is the same or similar to the prototype by copying the prototype object. Here, the prototype instance specifies the kind of object to be created. Creating objects in this way is very efficient, you don't need to know the details of object creation at all. For example, the installation of a Windows operating system is usually time-consuming, and copying is much faster.

Advantages of prototype mode:

  • Java's own prototype mode is based on the copy of the binary stream of memory, which is better in performance than directly new an object.
  • You can use the deep cloning method to save the state of the object, use the prototype mode to copy the object, and save its state, which simplifies the process of creating the object for use when needed (for example, to restore to a certain historical state). Aid to realize the undo operation.

Disadvantages of prototype mode:

  • Need to configure a clone method for each class
  • The clone method is located inside the class. When transforming an existing class, the code needs to be modified, which violates the opening and closing principle.
  • When implementing deep cloning, you need to write more complex code, and when there are multiple nested references between objects, in order to achieve deep cloning, the classes corresponding to each layer of objects must support deep cloning, which will be more troublesome to implement. Therefore, deep cloning and shallow cloning need to be used properly.

3. The structure and realization of the prototype pattern

Since Java provides the clone() method of the object, it is very simple to implement the prototype mode in Java.

  • The structure of the model (the prototype model contains the following main roles)
  1. Abstract prototype class: specifies the interface that the concrete prototype object must implement.
  2. Concrete prototype class: implements the clone() method of the abstract prototype class, which is an object that can be copied.
  3. Access class: Use the clone() method in the concrete prototype class to copy the new object.

The structure diagram is shown in Figure 1.

Insert picture description here

  • Prototype cloning is divided into shallow cloning and deep cloning.

    • Shallow clone: ​​Create a new object. The properties of the new object are exactly the same as the original object. For non-basic type properties, it still points to the memory address of the object pointed to by the original property.
    • Deep clone: ​​Create a new object, other objects referenced in the attribute will also be cloned, and no longer point to the original object address.

3.1 Shallow copy

The Object class in Java provides the clone() method for shallow cloning. As long as the concrete prototype class implements the Cloneable interface, the shallow clone of the object can be realized. The Cloneable interface here is the abstract prototype class. The code is as follows:

//具体原型类
class Realizetype implements Cloneable {
    
    
    Realizetype() {
    
    
        System.out.println("具体原型创建成功!");
    }

    public Object clone() throws CloneNotSupportedException {
    
    
        System.out.println("具体原型复制成功!");
        return (Realizetype) super.clone();
    }
}

//原型模式的测试类
public class PrototypeTest {
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
    
    
        Realizetype obj1 = new Realizetype();
        Realizetype obj2 = (Realizetype) obj1.clone();
        System.out.println("obj1==obj2?" + (obj1 == obj2));
    }
}

The running results of the program are as follows:

The concrete prototype was successfully created!
The concrete prototype was successfully copied!
obj1==obj2?false

3.2 Deep copy

  1. Copy all basic data type member variable values ​​of the object
  2. Apply for storage space for all member variables of the reference data type, and copy the object referenced by each reference data type variable until all the objects reachable by the object, that is to say. Deep copy of an object requires a deep copy of the entire object (including reference data types)
  3. Method 1: Rewrite clone()
  4. Method 2: Deep copy through object serialization
  5. To use the clone() method, the class needs to implement the Cloneable interface, and to serialize it needs to implement the Serializable interface

Phone class

public class Phone implements Serializable,Cloneable {
    
    
 
    private Integer telNumber;
    private String phoneType;
 
    public Phone(Integer telNumber, String phoneType) {
    
    
        this.telNumber = telNumber;
        this.phoneType = phoneType;
    }
 
    public String getPhoneType() {
    
    
        return phoneType;
    }
 
    public void setPhoneType(String phoneType) {
    
    
        this.phoneType = phoneType;
    }
 
    public Integer getTelNumber() {
    
    
        return telNumber;
    }
 
    public void setTelNumber(Integer telNumber) {
    
    
        this.telNumber = telNumber;
    }
 
    @Override
    public String toString() {
    
    
        return "Phone{" +
                "telNumber=" + telNumber +
                ", phoneType='" + phoneType + '\'' +
                '}';
    }
 
    @Override
    protected Object clone() throws CloneNotSupportedException {
    
    
        return super.clone();
    }
}
public class StudentDeepClone implements Serializable,Cloneable {
    
    
 
    private int id;
    private String name;
    private Phone phone;
 
    public StudentDeepClone(int id, String name, Phone phone) {
    
    
        this.id = id;
        this.name = name;
        this.phone = phone;
    }
 
    public int getId() {
    
    
        return id;
    }
 
    public void setId(int id) {
    
    
        this.id = id;
    }
 
    public String getName() {
    
    
        return name;
    }
 
    public void setName(String name) {
    
    
        this.name = name;
    }
 
    public Phone getPhone() {
    
    
        return phone;
    }
 
    public void setPhone(Phone phone) {
    
    
        this.phone = phone;
    }
 
    @Override
    public String toString() {
    
    
        return "StudentDeepClone{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", phone=" + phone +
                '}';
    } 
}

Method 1: Rewrite the clone() method

//深拷贝 方式1 使用clone方法。里面的引用类型需要实现Cloneable接口
@Override
protected Object clone() throws CloneNotSupportedException {
    
    
	StudentDeepClone student = null;
	try {
    
    
		//实现对基本数据类型的拷贝
		student = (StudentDeepClone) super.clone();
		//实现对引用数据类型的拷贝
		student.setPhone((Phone)phone.clone());
	}catch (Exception e){
    
    
		e.printStackTrace();
	}
	return student;
}

Method 2: Use deep copy through object serialization

//深拷贝 2 使用序列化方式
public Object deepClone(){
    
    
	//使用流对象
	ByteArrayOutputStream bos = null;//字节数组输出流
	ObjectOutputStream oos = null;//对象输出流
	ByteArrayInputStream bis = null;//字节数组输入流
	ObjectInputStream ois = null;//对象输入流
	try {
    
    
		//序列化
		bos = new ByteArrayOutputStream();
		oos = new ObjectOutputStream(bos);
		oos.writeObject(this);//将当前对象以输出流的方式进行输出
 
		//反序列化
		bis = new ByteArrayInputStream(bos.toByteArray());
		ois = new ObjectInputStream(bis);
		StudentDeepClone s = (StudentDeepClone) ois.readObject();
		return s;
	}catch (Exception e){
    
    
		e.printStackTrace();
		return null;
	}finally {
    
    
		try {
    
    
			bos.close();
			oos.close();
			bis.close();
			ois.close();
		}catch (Exception e){
    
    
			e.printStackTrace();
		}
	}
}

4. Application Scenarios of Prototype Mode

Prototype mode is usually suitable for the following scenarios

  • The objects are the same or similar, that is, when only a few individual properties are different.
  • The cost of creating an object is relatively high, such as long initialization time, too much CPU usage, or too much network resources, and so on. Resources need to be optimized.
  • Creating an object requires cumbersome data preparation or access permissions, etc., and needs to improve performance or improve security.
  • This type of object is widely used in the system, and each caller needs to re-assign its properties.

In Spring, the prototype pattern is widely used, for example scope='prototype', JSON.parseObject()etc. are specific applications of the prototype pattern.

5. Extension of the prototype mode

The prototype mode can be extended to a prototype mode with a prototype manager, which adds a prototype manager PrototypeManager class on the basis of the prototype mode. This class uses HashMap to store multiple replicated prototypes, and the Client class can obtain the replicated prototypes from the manager’s get(String id) method. The structure diagram is shown in Figure 5.

Insert picture description here

Example: Use the prototype mode with prototype manager to generate prototypes containing graphics such as "circles" and "squares", and calculate their area. Analysis: In this example, because there are different graphics classes, such as "circle" and "square", their methods of calculating area are different, so a prototype manager is needed to manage them. Figure 6 shows its structure.

Insert picture description here

The program code is as follows:

import java.util.*;

interface Shape extends Cloneable {
    
    
    public Object clone();    //拷贝

    public void countArea();    //计算面积
}


// ==================================================

class Circle implements Shape {
    
    
    public Object clone() {
    
    
        Circle w = null;
        try {
    
    
            w = (Circle) super.clone();
        } catch (CloneNotSupportedException e) {
    
    
            System.out.println("拷贝圆失败!");
        }
        return w;
    }

    public void countArea() {
    
    
        int r = 0;
        System.out.print("这是一个圆,请输入圆的半径:");
        Scanner input = new Scanner(System.in);
        r = input.nextInt();
        System.out.println("该圆的面积=" + 3.1415 * r * r + "\n");
    }
}
// ==================================================
class Square implements Shape {
    
    
    public Object clone() {
    
    
        Square b = null;
        try {
    
    
            b = (Square) super.clone();
        } catch (CloneNotSupportedException e) {
    
    
            System.out.println("拷贝正方形失败!");
        }
        return b;
    }

    public void countArea() {
    
    
        int a = 0;
        System.out.print("这是一个正方形,请输入它的边长:");
        Scanner input = new Scanner(System.in);
        a = input.nextInt();
        System.out.println("该正方形的面积=" + a * a + "\n");
    }
}
class ProtoTypeManager {
    
    
    private HashMap<String, Shape> ht = new HashMap<String, Shape>();

    public ProtoTypeManager() {
    
    
        ht.put("Circle", new Circle());
        ht.put("Square", new Square());
    }

    public void addshape(String key, Shape obj) {
    
    
        ht.put(key, obj);
    }

    public Shape getShape(String key) {
    
    
        Shape temp = ht.get(key);
        return (Shape) temp.clone();
    }
}
public class ProtoTypeShape {
    
    
    public static void main(String[] args) {
    
    
        ProtoTypeManager pm = new ProtoTypeManager();
        Shape obj1 = (Circle) pm.getShape("Circle");
        obj1.countArea();
        Shape obj2 = (Shape) pm.getShape("Square");
        obj2.countArea();
    }
}

6. Summary

  1. When creating a new object is more complicated, you can use the prototype mode to simplify the object creation process and improve efficiency.
  2. No need to reinitialize the object, but dynamically obtain the running state of the object.
  3. If the original object changes, other cloned objects will also change accordingly, without modifying the code.

Guess you like

Origin blog.csdn.net/saienenen/article/details/111661877