原型模式

1 什么是原型模式

原型模式就是将一个对象A克隆一份得到对象B,对象B与对象A完全一样,并且对象B的修改不会影响到对象A。说白了就是对象的克隆。

2 原型模式的实现

要实现原型模式,对于Java语言,只需要完成如下两步就可以:
(1)让对象实现Cloneable接口
(2)重写clone()方法
假如我们有一个Dog类,让它实现Cloneable接口:

public class Dog implements Cloneable {
    private String name;
    private Date birthday;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public Dog() {
    }

    public Dog(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

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

    public Date getBirthday() {
        return birthday;
    }

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

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

现在我们要实现克隆:

public static void main(String[] args) throws CloneNotSupportedException {
    // 原型对象dog1
    Date date = new Date();
    Dog dog1 = new Dog("二哈", date);
    System.out.println("dog1:" + dog1);
    System.out.println("dog1 hashcode:" + dog1.hashCode());

    // dog2克隆dog1
    Dog dog2 = (Dog) dog1.clone();
    System.out.println("dog2:" + dog2);
    System.out.println("dog2 hashcode:" + dog2.hashCode());

    // 修改dog2不影响dog1
    dog2.setName("克隆的二哈");
    System.out.println("dog1:" + dog1);
    System.out.println("dog2:" + dog2);
}

输出结果:

dog1:Dog{name='二哈', birthday=Thu May 28 22:38:17 CST 2020}
dog1 hashcode:329611835
dog2:Dog{name='二哈', birthday=Thu May 28 22:38:17 CST 2020}
dog2 hashcode:1347870667
dog1:Dog{name='二哈', birthday=Thu May 28 22:38:17 CST 2020}
dog2:Dog{name='克隆的二哈', birthday=Thu May 28 22:38:17 CST 2020}

可以看到dog1和dog2的name和birthday都是相同的,但是hashcode不同,说明是两个对象,并且修改dog2的name不会影响到dog1的值。

3 深克隆和浅克隆

然而上面的克隆方式如果修改的是引用类型对象,那么原型对象和克隆对象都会受影响:

public static void main(String[] args) throws CloneNotSupportedException {
    // 原型对象dog1
    Date birthday = new Date();
    Dog dog1 = new Dog("二哈", birthday);
    Dog dog2 = (Dog) dog1.clone();
    System.out.println("dog1:" + dog1);
    System.out.println("dog2:" + dog2);
    // 修改birthday的值
    birthday.setTime(12121212);
    System.out.println("----------------");
    System.out.println("dog1:" + dog1);
    System.out.println("dog2:" + dog2);
}

输出结果:

dog1:Dog{name='二哈', birthday=Thu May 28 22:56:29 CST 2020}
dog2:Dog{name='二哈', birthday=Thu May 28 22:56:29 CST 2020}
----------------
dog1:Dog{name='二哈', birthday=Thu Jan 01 11:22:01 CST 1970}
dog2:Dog{name='二哈', birthday=Thu Jan 01 11:22:01 CST 1970}

可以看到修改birthday的值之后,dog1和dog2的值都变化了。这是因为clone只是把引用对象的地址拷贝了一份,并没有重新创建个对象,所以dog1和dog2的birthday指向的是同一个对象,这就是浅克隆。
为了解决这个问题,我们需要对Dog的clone方法做一些修改:

@Override
protected Object clone() throws CloneNotSupportedException {
    Object object = super.clone();
    
    // 将对象的属性也克隆
    Dog dog = (Dog) object;
    dog.birthday = (Date) this.birthday.clone();
    
    return dog;
}

这时我们在运行上面的代码的结果如下:

dog1:Dog{name='二哈', birthday=Thu May 28 23:05:03 CST 2020}
dog2:Dog{name='二哈', birthday=Thu May 28 23:05:03 CST 2020}
----------------
dog1:Dog{name='二哈', birthday=Thu Jan 01 11:22:01 CST 1970}
dog2:Dog{name='二哈', birthday=Thu May 28 23:05:03 CST 2020}

我们发现修改birthday的值只会影响dog1,而不会影响到dog2,这样就能保证Dog的克隆都是深克隆。当然,上述代码中对属性的克隆也是一次浅克隆,如果想实现完全的深克隆,可以是使用序列化和反序列化来实现,但是序列化和反序列化需要和IO流打交道,效率自然比不过Cloneable接口,所以在具体实现原型模式时,需要按照具体场景选择合适的实现方法。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容