小唐说设计模式————原型模式

什么是原型模式

原型模式(Prototype Pattern)是五种创建型模式的其中一种,用原型实例指定创建对象的种类作为原型,并且通过拷贝原型来创建新的对象。

为什么要使用原型模式

新建一个对象有时候会很麻烦,可能涉及大量的变量初始化,函数、代码块的执行,不仅浪费资源,还会涉及数据准备、访问权限等操作。

实现

原型模式至少涉及两个角色:

  • Prototype,即原型类:需要实现Cloneable接口,并重写clone()方法,为外部提供一个克隆自身的方法;
  • Client,即应用类:让一个原型克隆自身从而创建一个新的对象。
public class Prototype implements Cloneable {
   //自身的一些方法...
    @Override
    public Object clone() throws CloneNotSupportedException {
        Prototype proto = (Prototype) super.clone();
        return proto;
    }
}
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Prototype prototype = new Prototype();
        Prototype prototype1 = (Prototype) prototype.clone();
       	...
    }
}

为了探讨其中的一些细节,我们对上面的代码稍微添加一些细节部分:

public class Prototype implements Cloneable {
    private String name;

    public Prototype() {
        System.out.println("empty constructor--------------------");
    }
  
    public Prototype(String name) {
        System.out.println("constructor--------------------");
        this.name = name;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Prototype proto = (Prototype) super.clone();
        return proto;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return super.toString() + " " + this.getName();
    }
}

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Prototype prototype = new Prototype();
        prototype.setName("111");
        System.out.println(prototype.toString());
        Prototype prototype1 = (Prototype) prototype.clone();
        System.out.println(prototype1.toString());
        prototype1.setName("222");
        System.out.println(prototype.toString());
        System.out.println(prototype1.toString());
    }
}

在Prototype类中加入了构造函数,重写了toString(),运行main(),结果如下:

empty constructor--------------------
com.company.Prototype@4554617c 111
com.company.Prototype@74a14482 111
com.company.Prototype@4554617c 111
com.company.Prototype@74a14482 222

可以看出:

构造函数只执行了一次,说明复制原型类的对象并不需要执行构造函数;
原对象和复制对象的地址不同,说明是重新创建的;
目前看来,在复制完成之后,复制的对象似乎和原对象就没有关系了,原对象的改变不会影响复制的对象,复制的对象改变也不会影响原对象。

真的是这样吗?

我们修改一下代码:

public class Car {
    private String color;

    public Car() {
    }

    public Car(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

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

public class Prototype implements Cloneable {
    private String name;
    private Car car;

    public Prototype() {
        System.out.println("empty constructor--------------------");
    }

    public Prototype(String name) {
        System.out.println("constructor--------------------");
        this.name = name;
    }

    public Prototype(String name, Car car) {
        this.name = name;
        this.car = car;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        Prototype proto = (Prototype) super.clone();
        return proto;
    }

    public String getName() {
        return name;
    }

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

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return super.toString() + " " + this.getName() + " " + this.getCar().getColor() + " Car" +  " " + this.getCar().toString();
    }
}

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Prototype prototype = new Prototype();
        Car car1 = new Car("red");
        Car car2 = new Car("yel");
        prototype.setName("111");
        prototype.setCar(car1);
        System.out.println(prototype.toString());
        Prototype prototype1 = (Prototype) prototype.clone();
        System.out.println(prototype1.toString());
        prototype1.setName("222");
        prototype1.setCar(car2);
        System.out.println(prototype.toString());
        System.out.println(prototype1.toString());
    }
}

执行结果:

empty constructor--------------------
com.company.Prototype@4554617c 111 red Car	com.company.Car@74a14482
com.company.Prototype@1540e19d 111 red Car	com.company.Car@74a14482
com.company.Prototype@4554617c 111 red Car	com.company.Car@74a14482
com.company.Prototype@1540e19d 222 yel Car	com.company.Car@677327b6

可以看过结果中第4行和第5行,Car的颜色和地址都不一样,我几乎可以得出结论它们确实不是一个对象了,但是这样写其实是有漏洞的,我通过setCar()是显式地改变了这个对象,不能正确得出结论。

再将输出改一下:

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Prototype prototype = new Prototype();
        Car car1 = new Car("red");
        prototype.setName("111");
        prototype.setCar(car1);
        System.out.println(prototype.toString());
        Prototype prototype1 = (Prototype) prototype.clone();
        System.out.println(prototype1.toString());
        prototype1.setName("222");
        prototype1.getCar().setColor("black");
        System.out.println(prototype.toString());
        System.out.println(prototype1.toString());
    }
}

结果变为:

empty constructor--------------------
com.company.Prototype@4554617c 111 red Car com.company.Car@74a14482
com.company.Prototype@1540e19d 111 red Car com.company.Car@74a14482
com.company.Prototype@4554617c 111 black Car com.company.Car@74a14482
com.company.Prototype@1540e19d 222 black Car com.company.Car@74a14482

所有的Car对象的地址都是同一个,颜色值也一同改变,说明clone()实现的是浅拷贝。

浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型仍指向的还是原对象所指向的地址。 深复制:将一个对象复制后,基本数据类型和引用类型都重新创建。

如果想对引用类型也实现深拷贝,可以采用二进制流读写等操作实现,此处不再详述。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值