Learning Java Architecture Design Patterns from the Beginning 3: Prototype Patterns

Prototype pattern - A prototype instance specifies the type of object created and creates new objects by copying these prototypes. The caller does not need to know any creation details and does not call the constructor! It belongs to the creation mode.

It does not create other objects by new, but by using existing objects in memory as templates, without using constructors.

There are many prototype implementation schemes, such as beanutils, json, guava, cloneable interface for JDK implementation (deep cloning) and so on. They do not need to care about their implementation, just use them directly.

It can be divided into shallow clone and deep clone.

Shallow cloning: Copy the reference of this object, that is to say, if the original object's value changes, the value of the duplicated object will also change.

Deep Cloning: The value of this object is copied and created through bytecode construction.

Among them, Spring is realized by configuration, which is generally shallow cloning, and most of them are shallow cloning.

It conflicts with the singleton pattern. If your object needs to be created every time it is used, you can use the prototype pattern. If you can use the same object every time, you don't need to use the prototype pattern!

Shallow cloning example——

First create the Prototype interface:

package com.pansoft.com.prototype;

public interface prototype {
    prototype clone();

}

 

Create an object ConcretePrototype that specifically needs to be cloned:

package com.pansoft.com.prototype;

import java.util.List;

public class ConcretePrototypeA implements prototype{
    private int age;
    private String name;
    private List hobbies;
    
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public List getHobbies() {
        return hobbies;
    }

    public void setHobbies(List hobbies) {
        this.hobbies = hobbies;
    }

    public prototype clone(){
        ConcretePrototypeA concretePrototypeA = new ConcretePrototypeA();
        concretePrototypeA.setAge(this.age);
        concretePrototypeA.setName(this.name);
        concretePrototypeA.setHobbies(this.hobbies);
        return null;
    }

}

It inherits the Prototype interface. big implements the clone() method. In the clone method, new is an object of the ConcretePrototype A class.

 

Create Client objects:

package com.pansoft.com.prototype;

public class Client {
    /*private prototype pro;
    public Client(prototype pro){
        this.pro = pro;
    }*/
    
    public prototype startClone(prototype concretePrototype){
        return concretePrototype.clone();
    }

}

In it, the clone method is invoked through the object of the prototype interface.

 

Test:

package com.pansoft.com.prototype;

import java.util.ArrayList;
import java.util.List;

public class prototypeTest {
    public static void main(String[] args){
        ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
        concretePrototype.setAge(18);
        concretePrototype.setName("Tom");
        List hobbies = new ArrayList<String>();
        concretePrototype.setHobbies(hobbies);
        
        Client client = new Client();
        ConcretePrototypeA copy = (ConcretePrototypeA)client.startClone(concretePrototype);
        System.out.println(copy);
        
        System.out.println("The value of the reference type address in the cloned object:"+copy.getHobbies());
        System.out.println("The value of the reference type address in the original object:"+concretePrototype.getHobbies());
        
        //Equality copy It's not the value, it's the address. That's shallow cloning.
        //There are risks in this way.
        System.out.println("Object address comparison:"+(copy.getHobbies() == concretePrototype.getHobbies()));
        
    }

}

But the problem here is that clone() methods are still objects obtained by new methods and constructors! I didn't understand, did I?

 

Deep Cloning Example——

Create a prototype monkey Monkey class:

package com.pansoft.com.prototype.deep;

import java.util.Date;

public class Monkey {
    public int height;
    public int weight;
    public Date birthday;

}

It has three attributes, height, width and birth date.

 

Create reference object JinGuBang class:

package com.pansoft.com.prototype.deep;

import java.io.Serializable;

public class JinGuBang implements Serializable {
    public float h = 100;
    public float d = 10;
    public void big(){
        this.d *= 2;
        this.h *= 2;
    }
    public void small(){
        this.d /= 2;
        this.h /= 2;
    }

}

It inherits the Serializable interface, defines two variables and initializes them. It also defines two methods, one is bigger and the other is smaller.

 

Create specific objects Qitian Dasheng class:

package com.pansoft.com.prototype.deep;

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

public class QiTianDaSheng extends Monkey implements Cloneable, Serializable {
    public JinGuBang jinGuBang;
    public QiTianDaSheng(){
        //Just initialize
        this.birthday = new Date();
        this.jinGuBang = new JinGuBang();
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException{
        return this.deepClone();
    }
    
    public Object deepClone(){
        try{
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            
            QiTianDaSheng copy = (QiTianDaSheng)ois.readObject();
            copy.birthday = new Date();
            return copy;
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
    }
    
    public QiTianDaSheng shallowColne(QiTianDaSheng target){
        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        qiTianDaSheng.height = target.height;
        qiTianDaSheng.weight = target.weight;
        
        qiTianDaSheng.jinGuBang = target.jinGuBang;
        qiTianDaSheng.birthday = new Date();
        
        return qiTianDaSheng;
    }

}

It inherits the Monkey class and Cloneable, Serializable interfaces, where the clone() method is deep cloning and the shallow Clone (QiTianDaSheng target) method is shallow cloning.

In deep cloning, QiTian DaSheng's objects are obtained by bytecode.

 

Test:

package com.pansoft.com.prototype.deep;

public class DeepCloneTest {
    public static void main(String[] args){
        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        try{
            QiTianDaSheng clone = (QiTianDaSheng)qiTianDaSheng.clone();
            System.out.println("Deep cloning:"+(qiTianDaSheng.jinGuBang == clone.jinGuBang));
        }catch(Exception e){
            e.printStackTrace();
        }
        
        QiTianDaSheng q = new QiTianDaSheng();
        QiTianDaSheng n = q.shallowColne(q);
        System.out.println("Shallow cloning:"+(q.jinGuBang == n.jinGuBang));
    }

}

It was found that the two objects of deep cloning were not equal and the shallow cloning was equal. Depreciation means that if the target of cloning is a singleton, then deep cloning will destroy the singleton. How can we prevent deep cloning from destroying the singleton?

Either the singleton class does not implement the Cloneable interface, or, as in the previous section, overrides the clone method and returns the singleton object in the clone method - return INSTANCE;

 

Our commonly used ArrayList implements the Cloneable interface!

Keywords: PHP Java JSON JDK Spring

Added by dimitris on Mon, 29 Jul 2019 15:43:56 +0300