打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
创建型-原型模式 ( Prototype Pattern)

  上一篇咱们学习的是建造者模式,有的小伙伴会觉得比较复杂和难以理解。今天咱们来学习一个简单的设计模式-原型模型。

  什么是原型模式

  原型模式的核心就是克隆对象,将一个对象作为拷贝对象(原型),然后不断的创建出新的对象。

  原型模式的应用场景还是比较多的,例如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 + '}';
 }
}12345678910111213141516171819202122232425262728293031323334353637383940414243复制代码类型:[java]

  第二步:创建测试类并查看结果

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());
 }
}1234567891011121314复制代码类型:[java]

  测试结果:

调用软妹币构造函数
RMB: RMB{denomination=100, code=1} hashcode:1239731077RMB: RMB{denomination=100, code=2} hashcode:557041912123复制代码类型:[java]

  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 + '\'' + '}';
 }
}1234567891011121314151617181920212223242526272829复制代码类型:[java]

  第二步:修改软妹币对象

/**
 * 软妹币对象
 */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 + '}';
 }
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253复制代码类型:[java]

  第三步:创建测试类并查看结果

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());
 }
}123456789101112131415复制代码类型:[java]

  测试结果:

调用软妹币构造函数
RMB: RMB{denomination=100, code=1, operator=Operator{name='张三'}} operator:557041912RMB: RMB{denomination=100, code=2, operator=Operator{name='张三'}} operator:557041912123复制代码类型:[java]

  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 + '\'' + '}';
 }
}1234567891011121314151617181920212223242526272829303132333435复制代码类型:[java]

  第二步:重写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 + '}';
 }
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556复制代码类型:[java]

  第三步:查看测试结果

调用软妹币构造函数
RMB: RMB{denomination=100, code=1, operator=Operator{name='张三'}} operator:557041912RMB: RMB{denomination=100, code=2, operator=Operator{name='张三'}} operator:1134712904123复制代码类型:[java]

  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 + '\'' + '}';
 }
}12345678910111213141516171819202122232425262728293031复制代码类型:[java]

  第二步:让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 + '}';
 }
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374复制代码类型:[java]

  第三步:修改测试类并查看结果

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());
 }
}12345678910111213141516复制代码类型:[java]

  测试结果:

调用软妹币构造函数
RMB: RMB{denomination=100, code=1, operator=Operator{name='张三'}} operator:863831416RMB: RMB{denomination=100, code=2, operator=Operator{name='张三'}} operator:1836643189123复制代码类型:[java]

  ps:根据测试结果,operator对象也是两个不同的对象。

  总结

  优点:

  用法简单,上手难度低

  提高创建新对象的效率

  可实现不同时刻对象状态(如导入昨天日报)

  缺点:

  必须要实现Cloneable接口重写clone方法

  违背开闭原则

  深拷贝实现相对复杂

  引用类型如果是多层嵌套,则每一层对象都需要支持克隆

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
原型模式(Prototype Pattern)
博客园 - 探索设计模式(六):原型模式(Prototype Pattern)
C++设计模式-原型
PHP设计模式之原型模式
结合JDK源码看设计模式——原型模式
原型模式
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服