原型模式

概述

原型模式(Prototype Pattern)是指使用原型实例创建对象的类型,并且通过复制原型创建新的对象,属于创建型模式不属于23中设计模式。说白了就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。原型模式的例子:BeanUtils、JSON.parseObject等

原型模式适用场景:

  1. 类初始化消耗大
  2. new对象时需要非常繁琐的过程(数据准备、访问权限等)
  3. 构造函数比较复杂

对对象复制和拷贝分为浅复制和深复制下面来介绍

浅复制

完整复制值类型的数据,不复制引用类型的数据。换句话说就是只复制当前对象的值数据,对于内部数组、引用对象不复制,也就是说修改引用对象时,所以复制对象包括原型对象都会被修改。

下面我们实现JDK的Cloneable接口实现克隆

原型实体对象

public class ConcretePrototypeA implements Cloneable{
    private String name;
    private String age;
    private Date birthday;

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "ConcretePrototypeA{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", birthday=" + birthday +
                '}';
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();
        ConcretePrototypeA cloneObj = (ConcretePrototypeA)obj;
        return obj;
    }
}

使用Object的clone方法实现

测试代码

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        ConcretePrototypeA concretePrototypeA1 = new ConcretePrototypeA();
        concretePrototypeA1.setAge("20");
        concretePrototypeA1.setName("小明");
        concretePrototypeA1.setBirthday(new Date(21322212L));
        System.out.println("原型对象"+concretePrototypeA1);
        ConcretePrototypeA concretePrototypeA2 = (ConcretePrototypeA) concretePrototypeA1.clone();
        System.out.println("复制对象"+concretePrototypeA2);
        System.out.println("原型对象与复制对象是否相等:"+ (concretePrototypeA1 == concretePrototypeA2));
        System.out.println("原型对象Birthday与复制对象Birthday是否相等:"+(concretePrototypeA1.getBirthday() == concretePrototypeA2.getBirthday()));
        concretePrototypeA2.getBirthday().setTime(55455544L);
        System.out.println("原型对象"+concretePrototypeA1);
        System.out.println("复制对象"+concretePrototypeA2);

    }
}

结果

可以看到复制了一个新的对象,复制了值类型的属性,不过Date类型的Birthday属性没有复制仍然是同一个引用,我们修改复制对象的Birthday属性值时,元对象也会改变

 深复制

 除了复制值类型的属性,那些指向对象的引用也会复制指向一个新的对象。那么这是怎么实现的呢。我们举一个经典的例子齐天大圣。首先他是只猴子,手拿金箍棒,可变大小,一根猴毛吹出千万个泼猴。

原型猴子Monkey

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

引用类金箍棒jinGuBang

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

具体原型类QiTianDaShen

public class QiTianDaSheng extends Monkey implements Cloneable, Serializable {
    public JinGubang jingubang;

    public QiTianDaSheng()
    {
        this.height = "1.8m";
        this.weight = "130g";
        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();

            bos.close();
            oos.close();
            bis.close();
            ois.close();
            return copy;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }
    }
    public Object shallowClone() throws CloneNotSupportedException {
        return super.clone();
    }
}

测试类

public class DeepTest {
    public static void main(String[] args) {
        QiTianDaSheng q1 = new QiTianDaSheng();
        try {
            QiTianDaSheng q2 = (QiTianDaSheng)q1.clone();
            System.out.println("原型对象与复制对象里面的引用对象是否相等:"+(q1.jingubang == q2.jingubang));
        }catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

结果

实现序列化接口,把对象写到二进制流里然后在读出对象产生新的对象实现深度克隆

有没有发现原型模式与单例模式是互相冲突的,所以为防止克隆破坏单例时,我们禁止深度克隆。要么单例类不实现Cloneable接口;要么重写clone方法,在clone方法里直接返回单例对象就行。

 

参考:https://en.wikipedia.org/wiki/Prototype_pattern

posted @ 2019-03-26 16:41  dum  阅读(256)  评论(0编辑  收藏  举报