技术专栏23天学习23种设计模式程序员

23天学习23种设计模式——原型模式

2017-10-08  本文已影响62人  soberbad

前言

类似于《西游记》中的孙悟空拔出猴毛,根据自己的样子变出很多猴子来。或者是《火影忍者》中鸣人使用影分身变出很多个鸣人来。设计模式中的原型模式,也是根据原型(如同孙悟空,鸣人本人就是原型)创建出新的对象。

是什么

原型模式(prototype pattern)是一种创建型模式,使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式UML类图

为什么

原型模式多用于创建复杂的或构造耗时的实例,这样就能高效地创建实例对象,减轻创建对象的成本。

怎么做

在Java中,所有的类都继承自java.lang.Object类,而该类提供了clone()方法,这个本地方法可以将Java对象复制一份。但是需要注意的是,必须要实现标识接口Cloneable来标识这个类可以被复制。

clone()方法

下面通过一个例子来实现原型模式,这是一个实现了Cloneable接口的类,并重载了Object类的clone方法。

/**
 * 实现Cloneable接口表示该类可以被复制
 */
public class Sheep implements Cloneable{

    private String name;
    private Date birthDate;

    public Sheep(String name,Date date) {
        this.name = name;
        this.birthDate = date;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public String getName() {
        return name;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }

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

    /**
     * 重载Object类的clone方法
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //  浅复制
        return super.clone();
    }

    @Override
    public String toString() {
        return "Sheep<"+hashCode()+">'s name="+name+" birthdate:"
                +birthDate.toString()+" name hashcode:"+name.hashCode()+" birthdate hashcode:"+birthDate.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        Sheep another = (Sheep) obj;
        return this.name.equals(another.name)&&this.birthDate.compareTo(another.birthDate)==0;
    }
}

现在我们来测试一下上面那个类对象的复制,

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {

        Date birthDate = new Date(1341314415234L);
        String name = "duoli";
        Sheep duoli = new Sheep(name,birthDate);

        Sheep cloneSheep = (Sheep) duoli.clone();

        System.out.println(duoli.toString());
        System.out.println(cloneSheep.toString());

        birthDate.setTime(2124124124L);

        System.out.println(duoli.toString());
        System.out.println(cloneSheep.toString());
    }
}
运行结果

从上面运行结果截图可以知道,原型对象和克隆对象的值是一样的,但是修改原型对象的属性之后,克隆对象的相应属性也被修改了。这是由于浅复制导致的。也就是说克隆对象只复制了原型对象的地址。当该地址对应的原型对象的值发生变化时,有着相同地址的克隆对象的属性也会发生变化。

浅复制

这个问题可以通过深复制来解决,在深复制中,除了对象本身被复制外,对象所有的属性也会被复制。

深复制

对于Sheep类,我们可以进行如下的修改:

  ...
   @Override
    protected Object clone() throws CloneNotSupportedException {
        //Deep Clone
        Sheep sheep = (Sheep) super.clone();
        sheep.birthDate = (Date) birthDate.clone();
        sheep.name = name;
        return sheep;
}
...

虽然,这种方式看起来比较简单,这是由于我们直接使用了引用类型Date类已经实现好了的clone方法。
对于我们自定义的类,往往我们要去实现Cloneable接口,并重写Object类的clone()方法。

我们还可以通过序列化的方式(Serialization)来实现。通过IO流操作把对象写入流中,流中的对象就是原有对象的拷贝,不仅复制了对象本身,而且可以复制其引用的成员属性。所以,将对象写入流中,然后从流中读出来,就能实现深复制了。

下面通过将clone方法中修改为序列化操作来实现深复制:

 @Override
    protected Object clone() throws CloneNotSupportedException {
        //Deep Clone
//        Sheep sheep = (Sheep) super.clone();
//        sheep.birthDate = (Date) birthDate.clone();
//        sheep.a = (A) a.clone();
//        sheep.name = name;
//        return sheep;

        try {
            return deepClone();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
}
 /**
     * 使用IO技术实现深复制
     * @return
     */
    public Sheep deepClone() throws IOException, ClassNotFoundException {

        //将对象写入IO流中
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(this);

        //将对象从IO流中取出来
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        Sheep clone = (Sheep) ois.readObject();

        baos.close();
        oos.close();
        bais.close();
        ois.close();

        return clone;
    }
序列化深复制拷贝
上一篇下一篇

猜你喜欢

热点阅读