设计模式---原型模式

打印 上一主题 下一主题

主题 777|帖子 777|积分 2331

简述


  • 类型:创建型
  • 目标:通过拷贝快速创建相同或相似对象。
接下来我们看一个需要改进的案例。
优化案例

话不多说,先来看一个创建相同或相似对象的传统写法。
原版v0
  1. public class Department {
  2.     private String name;
  3.     private String country;
  4.     private String province;
  5.     private String city;
  6.     private List<Employee> employees;
  7.     public String getName() {
  8.         return name;
  9.     }
  10.     public String getCountry() {
  11.         return country;
  12.     }
  13.     public String getProvince() {
  14.         return province;
  15.     }
  16.     public String getCity() {
  17.         return city;
  18.     }
  19.     public List<Employee> getEmployees() {
  20.         return employees;
  21.     }
  22.     public Department(String name, String country, String province,
  23.                       String city, List<Employee> employees) {
  24.         this.name = name;
  25.         this.country = country;
  26.         this.province = province;
  27.         this.city = city;
  28.         this.employees = employees;
  29.     }
  30. }
  31. class Employee {
  32.     private String name;
  33.     private String sex;
  34.     private int age;
  35.     private String country;
  36.     private String province;
  37.     private String city;
  38.     private String post;
  39.     public String getName() {
  40.         return name;
  41.     }
  42.     public String getSex() {
  43.         return sex;
  44.     }
  45.     public int getAge() {
  46.         return age;
  47.     }
  48.     public String getCountry() {
  49.         return country;
  50.     }
  51.     public String getProvince() {
  52.         return province;
  53.     }
  54.     public String getCity() {
  55.         return city;
  56.     }
  57.     public String getPost() {
  58.         return post;
  59.     }
  60.     public Employee(String name, String sex, int age,
  61.                     String country, String province,
  62.                     String city, String post) {
  63.         this.name = name;
  64.         this.sex = sex;
  65.         this.age = age;
  66.         this.country = country;
  67.         this.province = province;
  68.         this.city = city;
  69.         this.post = post;
  70.     }
  71. }
复制代码
已知一个Department类型的对象,我们想构造一个相似的对象。
  1. public static void main(String[] args) {
  2.     Employee emp = new Employee("张三", "男", 15, "中国", "江西省", "南昌市", "124-1241-1353");
  3.     Department department = new Department("开发部", "中国", "江西省", "南昌市", List.of(e)); // 已知对象
  4.     Department department1 = new Department(department.getName(), department.getCountry(), department.getProvince(), department.getCity(), department.getPost()); // 拷贝对象
  5. }
复制代码
可以感受到,对象拷贝的朴素写法非常的麻烦。而且想到每一处对象拷贝都需要这样写就感觉头皮发麻。
为了解决这个问题,我们引入原型模式。请看以下样例。
修改版v1(浅拷贝)
  1. public class Department {
  2.     private String name;
  3.     private String country;
  4.     private String province;
  5.     private String city;
  6.     private List<Employee> employees;
  7.     public Department(String name, String country, String province,
  8.                       String city, List<Employee> employees) {
  9.         this.name = name;
  10.         this.country = country;
  11.         this.province = province;
  12.         this.city = city;
  13.         this.employees = employees;
  14.     }
  15. }
  16. class Employee {
  17.     private String name;
  18.     private String sex;
  19.     private int age;
  20.     private String country;
  21.     private String province;
  22.     private String city;
  23.     private String post;
  24.     public Employee(String name, String sex, int age,
  25.                     String country, String province,
  26.                     String city, String post) {
  27.         this.name = name;
  28.         this.sex = sex;
  29.         this.age = age;
  30.         this.country = country;
  31.         this.province = province;
  32.         this.city = city;
  33.         this.post = post;
  34.     }
  35. }
复制代码
使用clone()方法拷贝目标对象。
  1. public static void main(String[] args) throws CloneNotSupportedException {
  2.     Employee e = new Employee("张三", "男", 15, "中国", "江西省", "南昌市", "124-1241-1353");
  3.     Department department = new Department("开发部", "中国", "江西省", "南昌市", List.of(e));
  4.     Department department1 = (Department)department.clone();
  5.     System.out.println(department == department1); // false
  6.     System.out.println(department.employees == department1.employees); // true
  7. }
复制代码
我们发现第8行输出true,这说明两个对象的employees的引用相同,这会导致修改其中一个employees的元素会影响到另一个,这并不好。
如何解决属性相同引用的问题?看以下样例。
修改版v2(深拷贝)
  1. public class Department implements Cloneable {
  2.     private String name;
  3.     private String country;
  4.     private String province;
  5.     private String city;
  6.     private List<Employee> employees;
  7.     public Department(String name, String country, String province,
  8.                       String city, List<Employee> employees) {
  9.         this.name = name;
  10.         this.country = country;
  11.         this.province = province;
  12.         this.city = city;
  13.         this.employees = employees;
  14.     }
  15.     @Override
  16.     public Object clone() throws CloneNotSupportedException {
  17.         Department department = (Department)super.clone();
  18.         List<Employee> emps = new ArrayList<>();
  19.         for (int i = 0; i < department.employees.size(); i ++) {
  20.             emps.add((Employee) employees.get(i).clone());
  21.         }
  22.         department.employees = emps;
  23.         return department;
  24.     }
  25. }
  26. class Employee implements Cloneable {
  27.     private String name;
  28.     private String sex;
  29.     private int age;
  30.     private String country;
  31.     private String province;
  32.     private String city;
  33.     private String post;
  34.     public Employee(String name, String sex, int age,
  35.                     String country, String province,
  36.                     String city, String post) {
  37.         this.name = name;
  38.         this.sex = sex;
  39.         this.age = age;
  40.         this.country = country;
  41.         this.province = province;
  42.         this.city = city;
  43.         this.post = post;
  44.     }
  45.     @Override
  46.     public Object clone() throws CloneNotSupportedException {
  47.         return super.clone();
  48.     }
  49. }
复制代码
使用clone() 拷贝对象,因为类以及类中的属性也重写了clone()。
  1. public static void main(String[] args) throws CloneNotSupportedException {
  2.     Employee e = new Employee("张三", "男", 15, "中国", "江西省", "南昌市", "124-1241-1353");
  3.     Department department = new Department("开发部", "中国", "江西省", "南昌市", List.of(e));
  4.     Department department1 = (Department)department.clone();
  5.     System.out.println(department == department1); // false
  6.     System.out.println(department.employees == department1.employees); // false
  7. }
复制代码
虽然这种方式可以深拷贝,但是这会让代码量激增。
序列化与反序列化可以解决这个问题。
修改版v3(序列化与反序列化)(推荐使用)
  1. public class Department {
  2.     private String name;
  3.     private String country;
  4.     private String province;
  5.     private String city;
  6.     private List<Employee> employees;
  7.     public Department(String name, String country, String province,
  8.                       String city, List<Employee> employees) {
  9.         this.name = name;
  10.         this.country = country;
  11.         this.province = province;
  12.         this.city = city;
  13.         this.employees = employees;
  14.     }
  15. }
  16. class Employee {
  17.     private String name;
  18.     private String sex;
  19.     private int age;
  20.     private String country;
  21.     private String province;
  22.     private String city;
  23.     private String post;
  24.     public Employee(String name, String sex, int age,
  25.                     String country, String province,
  26.                     String city, String post) {
  27.         this.name = name;
  28.         this.sex = sex;
  29.         this.age = age;
  30.         this.country = country;
  31.         this.province = province;
  32.         this.city = city;
  33.         this.post = post;
  34.     }
  35. }
复制代码
序列化与反序列化的实现方式有很多种,本文使用Gson来实现。以下是样例。
  1. public static void main(String[] args) throws CloneNotSupportedException {
  2.     Employee e = new Employee("张三", "男", 15, "中国", "江西省", "南昌市", "124-1241-1353");
  3.     Department department = new Department("开发部", "中国", "江西省", "南昌市", List.of(e));
  4.     Gson gson = new Gson();
  5.     String s = gson.toJson(department);
  6.     Department department1 = s.fromJson(s, Department.class);
  7.     System.out.println(department == department1); // false
  8.     System.out.println(department.employees == department1.employees); // false
  9. }
复制代码
基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。
总结

优点


  • 由于是直接从内存中读取对象进行克隆,所以性能卓越。
  • 代码量不论是相较于传统写法要精简很多,尤其是序列化与反序列化的方式。
缺点


  • 代码的理解难度增加。尤其是深拷贝的理解较为复杂。
适用场景


  • 适用于只有细微参数变动的对象创建。
  • 适用于需要备份的场景。如,当业务执行过程中,某种情况下需要数据回滚的时候,提前备份可以使用。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

知者何南

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表