绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
设计模式---原型模式
2022-09-22 16:43:40

简述

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

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

优化案例

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

初版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 = ; 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. 适用于需要备份的场景。如,当业务执行过程中,某种情况下需要数据回滚的时候,提前备份可以使用。
分享好友

分享这个小栈给你的朋友们,一起进步吧。

重学 Java 设计模式
创建时间:2020-06-03 14:48:59
技术好就一定能写出好代码吗?不能!再漂亮的马桶放到厨房都略显尴尬!无论是家里装修还是上道开车,只有通过实战才能快速将理论转变为技能。毕竟设计模式也是源于 克里斯托佛·亚历山大 的著作 《建筑模式语言》。
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

栈主、嘉宾

查看更多
  • 小傅哥
    栈主

小栈成员

查看更多
  • 小雨滴
  • 马延龙
  • wojiuzhuai
  • 水煮陈福
戳我,来吐槽~