上一篇咱们学习的是建造者模式,有的小伙伴会觉得比较复杂和难以理解。今天咱们来学习一个简单的设计模式-原型模型。
什么是原型模式
原型模式的核心就是克隆对象,将一个对象作为拷贝对象(原型),然后不断的创建出新的对象。
原型模式的应用场景还是比较多的,例如Spring框架中@Scope("prototype"),项目中数据模型相似的不同业务订单,OA系统中的日报、周报等。
原型模式 ( Prototype Pattern)
这里以印钞机为例,印钞机在印刷"软妹币"时,面额和材料相同,但是编号不同,这时候我们就可以使用原型模式。
第一步:创建软妹币对象并实现Cloneable接口
/**
* 软妹币对象
*/
public class RMB implements Cloneable {
private int denomination;
private int code;
public RMB() {
System.out.println("调用软妹币构造函数");
}
public int getDenomination() {
return denomination;
}
public void setDenomination(int denomination) {
this.denomination = denomination;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
@Override
protected RMB clone() throws CloneNotSupportedException {
return (RMB) super.clone();
}
@Override
public String toString() {
return "RMB{" +
"denomination=" + denomination +
", code=" + code +
'}';
}
}
第二步:创建测试类并查看结果
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
RMB rmb1 = new RMB();
rmb1.setDenomination(100);
rmb1.setCode(1);
RMB rmb2 = rmb1.clone();
rmb2.setCode(2);
System.out.println("RMB: " + rmb1 + " hashcode:" + rmb1.hashCode());
System.out.println("RMB: " + rmb2 + " hashcode:" + rmb2.hashCode());
}
}
测试结果:
调用软妹币构造函数
RMB: RMB{denomination=100, code=1} hashcode:1239731077
RMB: RMB{denomination=100, code=2} hashcode:557041912
ps:到这里其实就完成了原型模式的落地了。这里需要注意的是原型对象需要实现Cloneable重写clone方法。如果不实现Cloneable接口但调用super.clone()会抛出异常。这个是Java早期设计上的问题,实现Cloneable只是标记当前类可以使用clone方法而已。
浅拷贝与深拷贝
上面软妹币对象中的属性都是简单的数据类型,如果这时候需要再加上操作员的信息呢?咱们来改造下上面的代码看看会出现什么效果。
第一步:创建操作员
/**
* 操作员对象 只保留name属性便于演示
*/
public class Operator {
private String name;
public Operator() {
}
public Operator(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Operator{" +
"name='" + name + '\'' +
'}';
}
}
第二步:修改软妹币对象
/**
* 软妹币对象
*/
public class RMB implements Cloneable {
private int denomination;
private int code;
private Operator operator;
public RMB() {
System.out.println("调用软妹币构造函数");
}
public int getDenomination() {
return denomination;
}
public void setDenomination(int denomination) {
this.denomination = denomination;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Operator getOperator() {
return operator;
}
public void setOperator(Operator operator) {
this.operator = operator;
}
@Override
protected RMB clone() throws CloneNotSupportedException {
return (RMB) super.clone();
}
@Override
public String toString() {
return "RMB{" +
"denomination=" + denomination +
", code=" + code +
", operator=" + operator +
'}';
}
}
第三步:创建测试类并查看结果
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
RMB rmb1 = new RMB();
rmb1.setDenomination(100);
rmb1.setCode(1);
rmb1.setOperator(new Operator("张三"));
RMB rmb2 = rmb1.clone();
rmb2.setCode(2);
System.out.println("RMB: " + rmb1 + " operator:" + rmb1.getOperator().hashCode());
System.out.println("RMB: " + rmb2 + " operator:" + rmb2.getOperator().hashCode());
}
}
测试结果:
调用软妹币构造函数
RMB: RMB{denomination=100, code=1, operator=Operator{name='张三'}} operator:557041912
RMB: RMB{denomination=100, code=2, operator=Operator{name='张三'}} operator:557041912
ps:根据测试结果,我们可以看出operator对象是同一个,这个是因为clone方法使用的是浅拷贝。
那什么是浅拷贝和深拷贝呢?
浅拷贝:克隆对象中所有的成员变量与原型对象的值相等,引用类型变量和克隆对象指向同一个对象。
深拷贝:无论原型对象的成员变量是基本数据类型还是引用类型,都将复制一份给克隆对象。
显然,在很多场景下浅拷贝不满足我们的需求。
深拷贝实现
方案一:
第一步:让Operator实现Cloneable接口
/**
* 操作员对象 只保留name属性便于演示
*/
public class Operator implements Cloneable {
private String name;
public Operator() {
}
public Operator(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Operator clone() throws CloneNotSupportedException {
return (Operator) super.clone();
}
@Override
public String toString() {
return "Operator{" +
"name='" + name + '\'' +
'}';
}
}
第二步:重写RMB中的clone方法
/**
* 软妹币对象
*/
public class RMB implements Cloneable {
private int denomination;
private int code;
private Operator operator;
public RMB() {
System.out.println("调用软妹币构造函数");
}
public int getDenomination() {
return denomination;
}
public void setDenomination(int denomination) {
this.denomination = denomination;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Operator getOperator() {
return operator;
}
public void setOperator(Operator operator) {
this.operator = operator;
}
@Override
protected RMB clone() throws CloneNotSupportedException {
RMB rmb = (RMB) super.clone();
rmb.setOperator(this.getOperator().clone());
return rmb;
}
@Override
public String toString() {
return "RMB{" +
"denomination=" + denomination +
", code=" + code +
", operator=" + operator +
'}';
}
}
第三步:查看测试结果
调用软妹币构造函数
RMB: RMB{denomination=100, code=1, operator=Operator{name='张三'}} operator:557041912
RMB: RMB{denomination=100, code=2, operator=Operator{name='张三'}} operator:1134712904
ps:根据测试结果,operator对象已经是两个对象了。
方案二:
第一步:让Operator实现Serializable接口
import java.io.Serializable;
/**
* 操作员对象 只保留name属性便于演示
*/
public class Operator implements Serializable {
private String name;
public Operator() {
}
public Operator(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Operator{" +
"name='" + name + '\'' +
'}';
}
}
第二步:让RMB实现Serializable接口,并自定义深度拷贝方法
import java.io.*;
/**
* 软妹币对象
*/
public class RMB implements Cloneable, Serializable {
private int denomination;
private int code;
private Operator operator;
public RMB() {
System.out.println("调用软妹币构造函数");
}
public int getDenomination() {
return denomination;
}
public void setDenomination(int denomination) {
this.denomination = denomination;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Operator getOperator() {
return operator;
}
public void setOperator(Operator operator) {
this.operator = operator;
}
@Override
protected RMB clone() throws CloneNotSupportedException {
return (RMB) super.clone();
}
public RMB deepClone() {
try {
// 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (RMB) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "RMB{" +
"denomination=" + denomination +
", code=" + code +
", operator=" + operator +
'}';
}
}
第三步:修改测试类并查看结果
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
RMB rmb1 = new RMB();
rmb1.setDenomination(100);
rmb1.setCode(1);
rmb1.setOperator(new Operator("张三"));
// 调用深度克隆方法
RMB rmb2 = rmb1.deepClone();
rmb2.setCode(2);
System.out.println("RMB: " + rmb1 + " operator:" + rmb1.getOperator().hashCode());
System.out.println("RMB: " + rmb2 + " operator:" + rmb2.getOperator().hashCode());
}
}
测试结果:
调用软妹币构造函数
RMB: RMB{denomination=100, code=1, operator=Operator{name='张三'}} operator:863831416
RMB: RMB{denomination=100, code=2, operator=Operator{name='张三'}} operator:1836643189
ps:根据测试结果,operator对象也是两个不同的对象。
总结
优点:
用法简单,上手难度低
提高创建新对象的效率
可实现不同时刻对象状态(如导入昨天日报)
缺点:
必须要实现Cloneable接口重写clone方法
违背开闭原则
深拷贝实现相对复杂
引用类型如果是多层嵌套,则每一层对象都需要支持克隆
以上就是开课吧小编为大家整理发布的“创建型-原型模式 ( Prototype Pattern)”一文,更多相关内容尽在开课吧广场Java教程频道。
