原型模式、深拷贝与浅拷贝

原型模式是用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。
类图:
在这里插入图片描述
在这里prototype是一个原型类,声明一个克隆自己的接口。
Concrete Prototype是具体的原型类,实现一个克隆自己的操作。
Client是让一个一个原型对象克隆自己,从而创建一个新的对象。


public class Dog implements Cloneable {
    private String name;
    private Integer age;

    public Dog(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Dog dog = null;
        try {
            dog = (Dog) super.clone();
        }catch (Exception e){
            e.printStackTrace();
        }
        return dog;
    }
}


public class Client {
    public static void main(String[] args) throws Exception {
        Dog dog = new Dog("Tom",10);
        //Dog dog2 = new Dog(dog.getName(),dog.getAge());
        //Dog dog3 = new Dog(dog.getName(),dog.getAge());
        //Dog dog4 = new Dog(dog.getName(),dog.getAge());
		//上面是传统的写法

        Dog dog1 = (Dog) dog.clone();
        Dog dog5 = (Dog) dog.clone();
        Dog dog6 = (Dog) dog.clone();
        Dog dog7 = (Dog) dog.clone();

        System.out.println(dog1);
        System.out.println(dog5);
        System.out.println(dog6);
        System.out.println(dog7);	//结果都是一样的狗
    }
}

上面代码用传统写法跟原型模式比较,传统的写法每次创建一个狗对象都需要明确的知道它的名字与年龄,然后通过构造方法往里面set,如果狗的属性有很多的话这样操作会比较繁琐,而下面那种只需要给第一只狗加入名字与年龄,之后的克隆可以不需要知道狗的各种细节,可直接克隆一只一模一样的狗(对象)。
上面的拷贝可以称为浅拷贝,对于数据类型是引用数据类型的成员变量,比如是一个数组、对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。演示一下:

// 加一个铃铛类来测试
public class LinDang implements Cloneable{
    private String shape;
    private String color;


    public LinDang(String shape, String color) {
        this.shape = shape;
        this.color = color;
    }

    public String getShape() {
        return shape;
    }

    public void setShape(String shape) {
        this.shape = shape;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "LinDang{" +
                "shape='" + shape + '\'' +
                ", color='" + color + '\'' +
                '}';
    }

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

    public static void main(String[] args) throws Exception {
        Dog dog = new Dog("Tom",10);
        LinDang linDang = new LinDang("圆形","红色");//创建一个铃铛
        dog.setLinDang(linDang);

        Dog dog1 = (Dog) dog.clone();

        System.out.println(dog);
        System.out.println(dog1);

        dog.getLinDang().setColor("黑色");
        System.out.println("只修改dog狗铃铛为黑色");

        System.out.println(dog);
        System.out.println(dog1);
        /**
         * 输出结果:
         * Dog{name='Tom', age=10, linDang=LinDang{shape='圆形', color='红色'}}
         * Dog{name='Tom', age=10, linDang=LinDang{shape='圆形', color='红色'}}
         * 只修改dog狗铃铛为黑色
         * Dog{name='Tom', age=10, linDang=LinDang{shape='圆形', color='黑色'}}
         * Dog{name='Tom', age=10, linDang=LinDang{shape='圆形', color='黑色'}}
        */

    }

本意是改dog的铃铛,结果dog1的铃铛也改了。而使用深拷贝可以解决这个问题。实现深拷贝有两种方式:一种是通过克隆来实现,一种是通过序列化来实现。
1.通过克隆来实现:稍微改一下上面Dog类的clone()方法就好了

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Dog dog = null;
        try {
            dog = (Dog) super.clone();
            dog.setLinDang((LinDang) dog.getLinDang().clone());
            //相当于手动再把LingDang对象也克隆一遍
        }catch (Exception e){
            e.printStackTrace();
        }
        return dog;
    }

2.通过序列化来实现:Dog类和LingDang类实现Serializable接口,在Dog类里添加深度克隆的方法。

public class Dog implements Serializable {
    private String name;
    private Integer age;
    private LinDang linDang;

    public Dog(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public Dog(String name, Integer age, LinDang linDang) {
        this.name = name;
        this.age = age;
        this.linDang = linDang;
    }

    public LinDang getLinDang() {
        return linDang;
    }

    public void setLinDang(LinDang linDang) {
        this.linDang = linDang;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

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

	 public Object deepClone()throws Exception{
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            //序列化
            bos= new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            return ois.readObject();
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }finally {
            //关闭流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            }catch (Exception e2){
                e2.printStackTrace();
            }
        }
    }

}

在mian方法测试,结果与第一种一致。原型模式需要为每一个类配备一个克隆方法,对已有的类进行改造时,需要修改其源代码,在这里就违背了ocp原则(对扩展开放,对修改关闭)。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值