Prototype Pattern

Keywords: Java Design Pattern

🍀 The following content is synchronously posted on my personal blog https://www.lvjguo.top😊

1 Introduction

Definition: specify the type of objects to be created with prototype instances, and create new objects by copying these prototypes.

Features: do not need to know any creation details, do not call the constructor

Type: create type

Applicable scenarios:

  • Class initialization consumes more resources
  • An object generated by new requires a very cumbersome process (data preparation, access rights, etc.)
  • Constructors are complex
  • When a large number of objects are produced in the loop body

advantage:

  • Higher performance than directly new an object
  • Simplify the creation process

Disadvantages:

  • Cloning methods must be provided, and the functions of the class need to be comprehensively considered
  • It is easy to introduce risks when cloning complex objects or complex transformation of cloned objects
  • Deep copy and shallow copy should be used properly

UML class diagram:

Prototype mode is mainly used for object replication. Its core class prototype needs to meet the following two conditions:

  1. Implement clonable interface. There is a clonable interface in the java language, which has only one function, that is, to inform the virtual machine at run time that the clone method can be safely used on the class that implements this interface. In the java virtual machine, only classes that implement this interface can be copied. Otherwise, CloneNotSupportedException will be thrown at runtime.
  2. Override the clone method in the Object class. In Java, the parent class of all classes is the Object class. There is a clone method in the Object class, which returns a copy of the Object. However, its scope is of protected type, and ordinary classes cannot call it. Therefore, the Prototype class needs to modify the scope of the clone method to public type.

2 example

Considering such a business scenario, Alibaba cloud is now doing activities and the ECS is greatly reducing prices. He needs to notify his users of this activity by e-mail. However, if the creation of each e-mail is very cumbersome, how should we send e-mail?

Specific prototype role: Mail

@Data
public class Mail implements Cloneable{
    private String name;   //Mail recipient
    private String emailAddress;   //mailing address
    private String content;  //Mail content
    
    @Override
    public String toString() {
        return "Mail{" +
                "name='" + name + '\'' +
                ", emailAddress='" + emailAddress + '\'' +
                ", content='" + content + '\'' +
                '}'+super.toString();
    }
    
    public Mail(){
        System.out.println("Mail Class Constructor");
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        System.out.println("clone mail object");
        return super.clone();
    }
}

Customer role: MailUtil send mail

public class MailUtil {
    //Send mail
    public static void sendMail(Mail mail){
        String outputContent = "towards{0}classmate,mailing address:{1},Mail content:{2},Mail sent successfully";
        System.out.println(MessageFormat.format(outputContent,mail.getName(),mail.getEmailAddress(),mail.getContent()));
    }
    
    //
    public static void saveOriginMailRecord(Mail mail){
        System.out.println("storage originMail record,originMail:"+mail.getContent());
    }
}

Client test class:

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Mail mail = new Mail();
        mail.setContent("Initialize template");
        System.out.println("initialization mail:"+mail);
        for(int i = 0;i < 10;i++){
            //Clone mail
            Mail mailTemp = (Mail) mail.clone();
            mailTemp.setName("full name"+i);
            mailTemp.setEmailAddress(i+"@qq.com");
            mailTemp.setContent("Alibaba cloud cloud servers are on sale. Come and buy them!");
            MailUtil.sendMail(mailTemp);
            System.out.println("Cloned mailTemp:"+mailTemp);
        }
        MailUtil.saveOriginMailRecord(mail);
    }
}

Operation results:

When it comes to cloning, we need to pay attention to deep copy and shallow copy. The clone method of Object class will only copy the basic data types in the Object, and will not copy arrays, container objects, reference objects, etc. This is shallow copy. If you want to implement deep copy, you must copy the arrays, container objects, reference objects, etc. in the prototype mode.

@Data
public class Pig implements Cloneable{
    private String name;
    private Date birthday;

    public Pig(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Pig pig = (Pig)super.clone();

        //Deep cloning
        pig.birthday = (Date) pig.birthday.clone();
        return pig;
    }
}

3 prototype and singleton mode

  • Prototype pattern: the core of prototype pattern is to clone objects in memory using clone method without calling constructor

  • Singleton mode: the core of singleton mode is to privatize the constructor, control external users, and cannot call the constructor to create objects at will

There is a contradiction between the two. If the singleton class implements the clonable interface, the class can call the clone method to create another instance object. Therefore, the prototype pattern destroys the singleton pattern.

Solution:

  1. Do not implement clonable interface: do not implement clonable interface in singleton mode and do not provide memory copy function
  2. Invoking singleton in clone: if the Cloneable interface must be implemented, then in the rewritten clone method, we call the method of obtaining the singleton class, do not copy the memory object, create a new instance object.

4 use model

  • ArrayList class
/**
 * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
 * elements themselves are not copied.)
 *
 * @return a clone of this <tt>ArrayList</tt> instance
 */
public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();//Clone an ArrayList
        //Copy the data from the prototype to the new ArrayList
        v.elementData = Arrays.copyOf(elementData, size);
        //Change the number of modifications to 0
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

Posted by Conjurer on Wed, 20 Oct 2021 18:30:23 -0700