打赏
Fork me on GitHub

原型模式

原型模式主要使用与以下场景:

  (1)类初始化消耗资源较多。

  (2)使用 new 生成一个对象需要非常繁琐的过程(数据准备、访问权限等)。

  (3)构造函数比较复杂。

  (4)在循环体中产生大量对象。

  在 spring 中,原型模式应用的非常广泛。例如 scope="prototype" ,我们常用的JSON.oarseObject() 也是一种原型模式。

浅克隆

  一个标准的原型模式代码应该这样设计,先创建一个 Prototype 接口

  public interface Prototype { Prototype clone(); } 

  创建具体需要克隆的类 ConcretePrototypeA:

@Data
public class ConcretePrototypeA implements Prototype {
    private int age;
    private String name;
    private List hobbies;
    @Override
    public Prototype clone() {
        ConcretePrototypeA concretePrototypeA = new ConcretePrototypeA();
        concretePrototypeA.setAge(this.age);
        concretePrototypeA.setName(this.name);
        concretePrototypeA.setHobbies(this.hobbies);
        return concretePrototypeA;
    }
}

  创建 Client 类:

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

  测试代码如下:

    @Test
    void PrototypeTest(){
        //创建一个具体的需要克隆的对象
        ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
        //天成属性,方便测试
        concretePrototype.setAge(18);
        concretePrototype.setName("prototype");
        List hoobies = new ArrayList<String>();
        concretePrototype.setHobbies(hoobies);
        System.out.println(concretePrototype);

        //创建client类准备克隆
        Client client = new Client(concretePrototype);
        ConcretePrototypeA concretePrototypeClone = (ConcretePrototypeA)client.startClone(concretePrototype);
        System.out.println(concretePrototypeClone);

        System.out.println("克隆对象中的引用类型地址值:" + concretePrototypeClone.getHobbies());
        System.out.println("原对象中的引用类型地址值:" + concretePrototype.getHobbies());
        System.out.println("对象地址比较:" + (concretePrototype.getHobbies() == concretePrototypeClone.getHobbies()));
    }

  从测试结果可以看出,hobbies 的引用地址是相同的,意味着赋值的不是指,而是引用的地址。这样的话,如果我们修改任意一个对象的属性值,则 concretePrototype 和 concretePrototypeClone 的 hobbies 的值都会改变,这就是我们常说的浅克隆。浅克隆只是完整复制了值类型数据,没有赋值引用对象。换言之,所有的应用对象仍然指向原来的对象,显然这不是我们想要的结果。

深克隆

  我们换一个场景,大家都知道七天大声,首先它是一个猴子,有七十二变,把一根毫毛就可以吹出千万个猴子,还有一个金箍棒,金箍棒可以变大变小。这就是我们耳熟能详的原型摩尔是的经典体现。

  创建原型猴子类 Monkey:

  

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

  创建引用对象金箍棒类:

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

  创建具体的对象七天大圣类

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 shallClone(QiTianDaSheng target){
        QiTianDaSheng qiTianDaSheng =   new QiTianDaSheng();
        qiTianDaSheng.height = target.height;
        qiTianDaSheng.weight = target.weight;

        qiTianDaSheng.jinGuBang = target.jinGuBang;
        qiTianDaSheng.birthday = target.birthday;
        return  qiTianDaSheng;
    }
}
QiTianDaSheng

  测试代码如下:

    @Test
    void DeepCloneTest(){
        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.shallClone(q);
        System.out.println("浅克隆:" + (q.jinGuBang == n.jinGuBang));
    }

  测试结果如下:

 

克隆破坏单例模式

  如果克隆的目标是单例对象,那么意味着深克隆会破坏单例模式。实际上防止克隆破坏单例模式的解决思路很简单,禁止深克隆便可。要么单例就不实现 Cloneable 接口,要么我们重写 clone() 方法,在clone() 方法中直接返回单例对象即可,具体代码如下:

 @Override public Object clone() throws CloneNotSupportedException{ return INSTANCE; } 

  clean() 方法源码:

    /**
     * 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();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

 

posted @ 2020-05-10 17:50  l-coil  阅读(197)  评论(0编辑  收藏  举报