设计模式之原型模式

原型模式概述

定义

原型模式定义如下:

Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.(用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。)

原型模式通用类图:
在这里插入图片描述原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,Java提供了一个Cloneable接口来标示这个对象是可拷贝的

具体案例

先看一个需求:
有一个通用的产品,有许多成员变量,除了baseInfo其余的都一样,代码如下:

package com.dq.designpattern.prototype.shallowCopy;

public class Product {

    private String part1;
    private Integer part2;
    //...省略了其它成员变量
    private BaseInfo baseInfo;

    public String getPart1() {
        return part1;
    }

    public Product(String part1, Integer part2, BaseInfo baseInfo) {
        this.part1 = part1;
        this.part2 = part2;
        this.baseInfo = baseInfo;
    }

    public void setPart1(String part1) {
        this.part1 = part1;
    }

    public Integer getPart2() {
        return part2;
    }

    public void setPart2(Integer part2) {
        this.part2 = part2;
    }

    public BaseInfo getBaseInfo() {
        return baseInfo;
    }

    public void setBaseInfo(BaseInfo baseInfo) {
        this.baseInfo = baseInfo;
    }

    @Override
    public String toString() {
        return super.hashCode()+" ] Product{" +
                "part1='" + part1 + '\'' +
                ", part2=" + part2 +
                ", baseInfo=" + baseInfo +
                '}';
    }
}
package com.dq.designpattern.prototype.shallowCopy;

public class BaseInfo {
    private String companyName;

    public BaseInfo(String companyName) {
        this.companyName = companyName;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    @Override
    public String toString() {
        return super.hashCode()+" ] BaseInfo{" +
                "companyName='" + companyName + '\'' +
                '}';
    }
}

我们在new这个对象时,每次都要进行大量的初始化加载工作,但是每一个对象除了baseInfo,别的信息都一样,这时候我们可以使用原型模式,每次不要new出对象,而是用第一个new出的对象进行不断克隆,克隆出来的对象再设置其baseInfo即可。

优点

  • 性能优良

    原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。

  • 逃避构造函数的约束

    这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。优点就是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。

使用场景

  • 资源优化场景

    类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。

  • 性能和安全要求的场景

    通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

  • 一个对象多个修改者的场景

    一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。

注意事项

  • 构造函数不会被执行

    一个实现了Cloneable并重写了clone方法的类A,有一个无参构造或有参构造B,通过new关键字产生了一个对象S,再然后通过S.clone()方式产生了一个新的对象T,那么在对象拷贝时构造函数B是不会被执行的。

  • clone与final

    要使用clone方法,类的成员变量上不要增加final关键字。

浅拷贝

浅拷贝代码如下:

package com.dq.designpattern.prototype.shallowCopy;

public class Product implements Cloneable{

    private String part1;
    private Integer part2;
    //...省略了其它成员变量
    private BaseInfo baseInfo;

    public String getPart1() {
        return part1;
    }

    public Product(String part1, Integer part2, BaseInfo baseInfo) {
        this.part1 = part1;
        this.part2 = part2;
        this.baseInfo = baseInfo;
    }

    public void setPart1(String part1) {
        this.part1 = part1;
    }

    public Integer getPart2() {
        return part2;
    }

    public void setPart2(Integer part2) {
        this.part2 = part2;
    }

    public BaseInfo getBaseInfo() {
        return baseInfo;
    }

    public void setBaseInfo(BaseInfo baseInfo) {
        this.baseInfo = baseInfo;
    }

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

    @Override
    public String toString() {
        return super.hashCode()+" ] Product{" +
                "part1='" + part1 + '\'' +
                ", part2=" + part2 +
                ", baseInfo=" + baseInfo +
                '}';
    }
}

我们来看测试代码:

package com.dq.designpattern.prototype.shallowCopy;

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        BaseInfo baseInfo = new BaseInfo("dddd");
        Product product = new Product("part1",2,baseInfo);
        Product clone = product.clone();
        System.out.println("原始对象:"+product);
        System.out.println("克隆对象:"+clone);
        product.setPart1("new part1");
        product.getBaseInfo().setCompanyName("qqqq");
        System.out.println("原始对象:"+product);
        System.out.println("克隆对象:"+clone);

    }
}

运行结果:
在这里插入图片描述通过两个对象的哈希码可以知道,克隆对象和原始对象不是一个对象,当修改原始对象的part1和baseInfo时,克隆对象的part1没有变化,但它的baseInfo却跟着变化了。

原因:

Java做了一个偷懒的拷贝动作,Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝。确实是非常浅,两个对象共享了一个私有变量,你改我改大家都能改,是一种非常不安全的方式,

深拷贝

进行深拷贝的时候,Product和BaseInfo两个类都要实现Cloneable接口并重写clone()方法。

package com.dq.designpattern.prototype.deepCopy;

public class BaseInfo implements Cloneable{
    private String companyName;

    public BaseInfo(String companyName) {
        this.companyName = companyName;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

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

    @Override
    public String toString() {
        return super.hashCode()+" ] BaseInfo{" +
                "companyName='" + companyName + '\'' +
                '}';
    }
}
package com.dq.designpattern.prototype.deepCopy;

public class Product implements Cloneable{

    private String part1;
    private Integer part2;
    //...省略了其它成员变量
    private BaseInfo baseInfo;

    public String getPart1() {
        return part1;
    }

    public Product(String part1, Integer part2, BaseInfo baseInfo) {
        this.part1 = part1;
        this.part2 = part2;
        this.baseInfo = baseInfo;
    }

    public void setPart1(String part1) {
        this.part1 = part1;
    }

    public Integer getPart2() {
        return part2;
    }

    public void setPart2(Integer part2) {
        this.part2 = part2;
    }

    public BaseInfo getBaseInfo() {
        return baseInfo;
    }

    public void setBaseInfo(BaseInfo baseInfo) {
        this.baseInfo = baseInfo;
    }

    @Override
    protected Product clone() throws CloneNotSupportedException {
        //return (Product)super.clone();
        Product product = (Product) super.clone();
        BaseInfo baseInfo = this.baseInfo.clone();
        product.setBaseInfo(baseInfo);
        return product;
    }

    @Override
    public String toString() {
        return super.hashCode()+" ] Product{" +
                "part1='" + part1 + '\'' +
                ", part2=" + part2 +
                ", baseInfo=" + baseInfo +
                '}';
    }
}

再看测试代码:

package com.dq.designpattern.prototype.deepCopy;

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        BaseInfo baseInfo = new BaseInfo("dddd");
        Product product = new Product("part1",2,baseInfo);
        Product clone = product.clone();
        System.out.println("原始对象:"+product);
        System.out.println("克隆对象:"+clone);
        product.setPart1("new part1");
        product.getBaseInfo().setCompanyName("qqqq");
        System.out.println("原始对象:"+product);
        System.out.println("克隆对象:"+clone);

    }
}

结果:
在这里插入图片描述
从结果看出:修改原始对象后,克隆对象的数据没有改变,说明两者完全独立,实现了深拷贝。

序列化实现深拷贝

实现Serializable接口,利用IO流

package com.dq.designpattern.prototype.serializable;

import java.io.Serializable;

public class BaseInfo implements Cloneable, Serializable {
    private String companyName;

    public BaseInfo(String companyName) {
        this.companyName = companyName;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

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

    @Override
    public String toString() {
        return super.hashCode()+" ] BaseInfo{" +
                "companyName='" + companyName + '\'' +
                '}';
    }
}
package com.dq.designpattern.prototype.serializable;

import com.dq.designpattern.builder.BianzhongBuilder.Person;

import java.io.*;

public class Product implements Cloneable, Serializable {

    private String part1;
    private Integer part2;
    //...省略了其它成员变量
    private BaseInfo baseInfo;

    public String getPart1() {
        return part1;
    }

    public Product(String part1, Integer part2, BaseInfo baseInfo) {
        this.part1 = part1;
        this.part2 = part2;
        this.baseInfo = baseInfo;
    }

    public void setPart1(String part1) {
        this.part1 = part1;
    }

    public Integer getPart2() {
        return part2;
    }

    public void setPart2(Integer part2) {
        this.part2 = part2;
    }

    public BaseInfo getBaseInfo() {
        return baseInfo;
    }

    public void setBaseInfo(BaseInfo baseInfo) {
        this.baseInfo = baseInfo;
    }

    @Override
    protected Product clone() {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);){
            oos.writeObject(this);
        } catch (IOException e) {
            e.printStackTrace();
        }
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        try (ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream)){
            Product product = (Product)ois.readObject();
            return product;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String toString() {
        return super.hashCode()+" ] Product{" +
                "part1='" + part1 + '\'' +
                ", part2=" + part2 +
                ", baseInfo=" + baseInfo +
                '}';
    }
}
package com.dq.designpattern.prototype.serializable;

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        BaseInfo baseInfo = new BaseInfo("dddd");
        Product product = new Product("part1",2,baseInfo);
        Product clone = product.clone();
        System.out.println("原始对象:"+product);
        System.out.println("克隆对象:"+clone);
        product.setPart1("new part1");
        product.getBaseInfo().setCompanyName("qqqq");
        System.out.println("原始对象:"+product);
        System.out.println("克隆对象:"+clone);

    }
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值