原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

所谓原型模式,就是 Java 中的克隆技术,以某个对象为原型,复制出新的对象。显然新的对象具备原型对象的特点,效率高(避免了重新执行构造过程步骤)。

我们在设计后端代码的工程结构的时候会定义分层领域模型(VO、DTO、DO等),Service 层调 Dao 层代码获取数据库数据,返回的对象模型是 DO,在 Service 需要把 DO 对象转换为 VO 对象,这个时候就需要使用到原型模式。

原型模式主要适用于以下场景:

  • 类初始化消耗资源较多;
  • 使用 new 产生一个对象需要非常繁琐的过程(数据准备、访问权限等);
  • 构造函数比较复杂;
  • 在循环体中生产大量对象。

对象克隆有如下实现方式:

  • 继承 Cloneable 接口,实现 clone 方法

  • 反射实现克隆,例子有 Apache BeanUtils、PropertyUtils,Spring BeanUtils

  • 序列化实现克隆(字节流、JSON)

浅克隆

对值类型的成员变量进行值的复制,对引用类型的成员变量仅仅复制引用,不复制引用的对象。对应的实现方式有 Java clone、反射。

浅克隆的写法如下所示:

  1. /**
  2. * 学校对象
  3. */
  4. public class School {
  5. private String schoolName;
  6. public School() {
  7. schoolName = new String("苏州小学");
  8. }
  9. // get set
  10. }
  11. /**
  12. * 学生对象
  13. */
  14. public class Student implements Cloneable {
  15. private String studentName;
  16. private School school;
  17. public Student() {
  18. studentName = new String("张三");
  19. school = new School();
  20. }
  21. @Override
  22. protected Object clone() throws CloneNotSupportedException {
  23. return super.clone();
  24. }
  25. // get set
  26. }

测试代码如下:

  1. private static void shallowClone() throws Exception {
  2. System.out.println("*************浅克隆*************");
  3. Student s1 = new Student();
  4. Student s2 = (Student) s1.clone();
  5. System.out.println(s1 == s2);
  6. System.out.println(s1.getSchool() == s2.getSchool());
  7. System.out.println(s1.getSchool());
  8. System.out.println(s2.getSchool());
  9. }

运行结果如下图所示。

image.png

从测试结果可以看出,school 的引用地址是相同的,这意味着复制的不是值,而是引用的地址。这样的话,如果我们修改任意一个对象的 school 的值,另外一个对象中的 school 的值也会改变,这就是我们常说的浅克隆。

深克隆

对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制。对应的实现方式有序列化。

深克隆的写法如下所示:

  1. public class School implements Serializable {
  2. private static final long serialVersionUID = 1L;
  3. private String schoolName;
  4. public School() {
  5. schoolName = new String("苏州小学");
  6. }
  7. // get set
  8. }
  9. public class Student implements Cloneable, Serializable {
  10. private static final long serialVersionUID = 1L;
  11. private String studentName;
  12. private School school;
  13. public Student() {
  14. studentName = new String("张三");
  15. school = new School();
  16. }
  17. @Override
  18. protected Object clone() throws CloneNotSupportedException {
  19. return super.clone();
  20. }
  21. /**
  22. * 利用序列化、反序列化实现深拷贝
  23. *
  24. * @return
  25. * @throws Exception
  26. */
  27. public Student deepClone() throws Exception {
  28. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  29. ObjectOutputStream oos = new ObjectOutputStream(bos);
  30. oos.writeObject(this);
  31. ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
  32. ObjectInputStream ois = new ObjectInputStream(bis);
  33. return (Student)ois.readObject();
  34. }
  35. // get set
  36. }

测试代码如下:

  1. private static void deepClone() throws Exception {
  2. System.out.println("*************深克隆*************");
  3. Student s1 = new Student();
  4. Student s2 = s1.deepClone();
  5. System.out.println(s1 == s2);
  6. System.out.println(s1.getSchool() == s2.getSchool());
  7. System.out.println(s1.getSchool());
  8. System.out.println(s2.getSchool());
  9. }

运行结果如下图所示。

image.png

深克隆也可以通过 JSON 工具来实现。

摘录:《Spring 5 核心原理与30个类手写实战》来自文艺界的Tom老师的书籍。

作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/bo7icd 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。