5

设计模式---原型模式 - buzuweiqi

 1 year ago
source link: https://www.cnblogs.com/buzuweiqi/p/16709147.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

设计模式---原型模式

  • 类型:创建型
  • 目标:通过拷贝快速创建相同或相似对象。

接下来我们看一个需要改进的案例。

话不多说,先来看一个创建相同或相似对象的传统写法。

public class Department { private String name; private String country; private String province; private String city; private List<Employee> employees; public String getName() { return name; } public String getCountry() { return country; } public String getProvince() { return province; } public String getCity() { return city; } public List<Employee> getEmployees() { return employees; } public Department(String name, String country, String province, String city, List<Employee> employees) { this.name = name; this.country = country; this.province = province; this.city = city; this.employees = employees; }}class Employee { private String name; private String sex; private int age; private String country; private String province; private String city; private String post; public String getName() { return name; } public String getSex() { return sex; } public int getAge() { return age; } public String getCountry() { return country; } public String getProvince() { return province; } public String getCity() { return city; } public String getPost() { return post; } public Employee(String name, String sex, int age, String country, String province, String city, String post) { this.name = name; this.sex = sex; this.age = age; this.country = country; this.province = province; this.city = city; this.post = post; }}

已知一个Department类型的对象,我们想构造一个相似的对象。

public static void main(String[] args) { Employee emp = new Employee("张三", "男", 15, "中国", "江西省", "南昌市", "124-1241-1353"); Department department = new Department("开发部", "中国", "江西省", "南昌市", List.of(e)); // 已知对象 Department department1 = new Department(department.getName(), department.getCountry(), department.getProvince(), department.getCity(), department.getPost()); // 拷贝对象}

可以感受到,对象拷贝的朴素写法非常的麻烦。而且想到每一处对象拷贝都需要这样写就感觉头皮发麻。

为了解决这个问题,我们引入原型模式。请看以下样例。

修改版v1(浅拷贝)

public class Department { private String name; private String country; private String province; private String city; private List<Employee> employees; public Department(String name, String country, String province, String city, List<Employee> employees) { this.name = name; this.country = country; this.province = province; this.city = city; this.employees = employees; }}class Employee { private String name; private String sex; private int age; private String country; private String province; private String city; private String post; public Employee(String name, String sex, int age, String country, String province, String city, String post) { this.name = name; this.sex = sex; this.age = age; this.country = country; this.province = province; this.city = city; this.post = post; }}

使用clone()方法拷贝目标对象。

public static void main(String[] args) throws CloneNotSupportedException { Employee e = new Employee("张三", "男", 15, "中国", "江西省", "南昌市", "124-1241-1353"); Department department = new Department("开发部", "中国", "江西省", "南昌市", List.of(e)); Department department1 = (Department)department.clone(); System.out.println(department == department1); // false System.out.println(department.employees == department1.employees); // true}

我们发现第8行输出true,这说明两个对象的employees的引用相同,这会导致修改其中一个employees的元素会影响到另一个,这并不好。

如何解决属性相同引用的问题?看以下样例。

修改版v2(深拷贝)

public class Department implements Cloneable { private String name; private String country; private String province; private String city; private List<Employee> employees; public Department(String name, String country, String province, String city, List<Employee> employees) { this.name = name; this.country = country; this.province = province; this.city = city; this.employees = employees; } @Override public Object clone() throws CloneNotSupportedException { Department department = (Department)super.clone(); List<Employee> emps = new ArrayList<>(); for (int i = 0; i < department.employees.size(); i ++) { emps.add((Employee) employees.get(i).clone()); } department.employees = emps; return department; }}class Employee implements Cloneable { private String name; private String sex; private int age; private String country; private String province; private String city; private String post; public Employee(String name, String sex, int age, String country, String province, String city, String post) { this.name = name; this.sex = sex; this.age = age; this.country = country; this.province = province; this.city = city; this.post = post; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); }}

使用clone() 拷贝对象,因为类以及类中的属性也重写了clone()

public static void main(String[] args) throws CloneNotSupportedException { Employee e = new Employee("张三", "男", 15, "中国", "江西省", "南昌市", "124-1241-1353"); Department department = new Department("开发部", "中国", "江西省", "南昌市", List.of(e)); Department department1 = (Department)department.clone(); System.out.println(department == department1); // false System.out.println(department.employees == department1.employees); // false}

虽然这种方式可以深拷贝,但是这会让代码量激增。

序列化与反序列化可以解决这个问题。

修改版v3(序列化与反序列化)(推荐使用)

public class Department { private String name; private String country; private String province; private String city; private List<Employee> employees; public Department(String name, String country, String province, String city, List<Employee> employees) { this.name = name; this.country = country; this.province = province; this.city = city; this.employees = employees; }}class Employee { private String name; private String sex; private int age; private String country; private String province; private String city; private String post; public Employee(String name, String sex, int age, String country, String province, String city, String post) { this.name = name; this.sex = sex; this.age = age; this.country = country; this.province = province; this.city = city; this.post = post; }}

序列化与反序列化的实现方式有很多种,本文使用Gson来实现。以下是样例。

public static void main(String[] args) throws CloneNotSupportedException { Employee e = new Employee("张三", "男", 15, "中国", "江西省", "南昌市", "124-1241-1353"); Department department = new Department("开发部", "中国", "江西省", "南昌市", List.of(e)); Gson gson = new Gson(); String s = gson.toJson(department); Department department1 = s.fromJson(s, Department.class); System.out.println(department == department1); // false System.out.println(department.employees == department1.employees); // false}

基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。

  1. 由于是直接从内存中读取对象进行克隆,所以性能卓越。
  2. 代码量不论是相较于传统写法要精简很多,尤其是序列化与反序列化的方式。
  1. 代码的理解难度增加。尤其是深拷贝的理解较为复杂。
  1. 适用于只有细微参数变动的对象创建。
  2. 适用于需要备份的场景。如,当业务执行过程中,某种情况下需要数据回滚的时候,提前备份可以使用。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK