美文网首页技术专栏23天学习23种设计模式程序员
23天学习23种设计模式——原型模式

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

作者: soberbad | 来源:发表于2017-10-08 15:55 被阅读62次

    前言

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

    是什么

    原型模式(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;
        }
    
    序列化深复制拷贝

    相关文章

      网友评论

        本文标题:23天学习23种设计模式——原型模式

        本文链接:https://www.haomeiwen.com/subject/urtryxtx.html