原型模式概述
定义
原型模式定义如下:
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);
}
}