簡述 類型:創建型 目標:通過拷貝快速創建相同或相似對象。 接下來我們看一個需要改進的案例。 優化案例 話不多說,先來看一個創建相同或相似對象的傳統寫法。 原版v0 public class Department { private String name; private String count ...
簡述
- 類型:創建型
- 目的:通過拷貝快速創建相同或相似對象。
接下來我們看一個需要改進的案例。
優化案例
話不多說,先來看一個創建相同或相似對象的傳統寫法。
最初版v0
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方法克隆對象。讓問題在編譯的時候暴露出來總是優於把問題留到運行時。
總結
優點
- 由於是直接從記憶體中讀取對象進行克隆,所以性能卓越。
- 代碼量不論是相較於傳統寫法要精簡很多,尤其是序列化與反序列化的方式。
缺點
- 代碼的理解難度增加。尤其是深拷貝的理解較為複雜。
適用場景
- 適用於只有細微參數變動的對象創建。
- 適用於需要備份的場景。如,當業務執行過程中,某種情況下需要數據回滾的時候,提前備份可以使用。
本文來自博客園,作者:buzuweiqi,轉載請註明原文鏈接:https://www.cnblogs.com/buzuweiqi/p/16709147.html