从头开始学Java架构之设计模式3:原型模式

原型模式-是指原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。调用者不需要知道任何创建细节,不调用构造函数!属于创建型模式。

它不通过new,而是通过内存中已有的对象为模板,不使用构造函数的方式创造出来其他对象。

已经有很多原型模式的实现方案,想beanutils、json、guava、实现JDK的cloneable接口(该方式为深克隆)等等,不需要关心他们的实现方式,直接用就好了。

主要分为浅克隆和深克隆!

浅克隆:复制的是这个对象的引用,也就是说原来对象的值改变的话,复制的对象的值也会跟着改变。

深克隆:复制的是这个对象的值,通过字节码的构建,把这个对象创建出来。

其中,Spring中是通过配置实现的,一般都是浅克隆,平时用的也大部分都是浅克隆。

它和单例模式相互冲突。如果你的对象每次使用的时候都要创建一下,就可以使用原型模式,如果每次都可以使用同一个对象,就不需要使用原型 模式!

浅克隆示例——

先创建原型Prototype接口:

package com.pansoft.com.prototype;

public interface prototype {
    prototype clone();

}

创建具体需要克隆的对象ConcretePrototype:

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;
    }

}

它继承了Prototype接口,big实现了clone()方法,在clone方法里new了一个ConcretePrototypeA类的对象。

创建Client对象:

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();
    }

}

在它里面通过prototype接口的对象调用clone方法。

测试:

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("克隆对象中引用类型地址的值:"+copy.getHobbies());
        System.out.println("原对象中引用类型地址的值:"+concretePrototype.getHobbies());
        
        //相等,说明copy的并不是值,而是地址,这就是浅克隆。
        //这种方式存在一定风险
        System.out.println("对象地址比较:"+(copy.getHobbies() == concretePrototype.getHobbies()));
        
    }

}

但是这里有个问题,就是clone()方法里仍然是通过new方法和构造函数来获得的对象!是我没理解对吗

深克隆示例——

创建原型猴子Monkey类:

package com.pansoft.com.prototype.deep;

import java.util.Date;

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

}

它有三个属性,高度,宽度和生日。

创建引用对象金箍棒JinGuBang类:

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;
    }

}

它继承了Serializable接口,定义了两个变量并初始化,还定义了两个方法,一个变大,一个变小。

创建具体的对象齐天大圣QiTianDaSheng类:

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(){
        //只是初始化
        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;
    }

}

它继承了Monkey类和Cloneable、Serializable接口,其中,里面的clone()方法是深克隆,shallowClone(QiTianDaSheng target)方法是浅克隆。

在深克隆里通过字节码的方式获得到了QiTianDaSheng的对象。

测试:

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("深克隆:"+(qiTianDaSheng.jinGuBang == clone.jinGuBang));
        }catch(Exception e){
            e.printStackTrace();
        }
        
        QiTianDaSheng q = new QiTianDaSheng();
        QiTianDaSheng n = q.shallowColne(q);
        System.out.println("浅克隆:"+(q.jinGuBang == n.jinGuBang));
    }

}

发现深克隆出来的两个对象不相等,浅克隆相等。折旧意味着如果克隆的目标的对象是单例对象,那么深克隆就会破坏单例,那么如何防止深克隆破坏单例么?

要么单例类不实现Cloneable接口,要么跟上节一样,重写clone方法,在clone方法中返回单例对象即可——即return INSTANCE;

我们常用的ArrayList就实现了Cloneable接口!

猜你喜欢

转载自www.cnblogs.com/yinyj/p/11266305.html