• 设计模式---原型模式


    简述

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

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

    优化案例

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

    最初版v0

    public class Department {
    private String name;
    private String country;
    private String province;
    private String city;
    private List employees;
    public String getName() {
    return name;
    }
    public String getCountry() {
    return country;
    }
    public String getProvince() {
    return province;
    }
    public String getCity() {
    return city;
    }
    public List getEmployees() {
    return employees;
    }
    public Department(String name, String country, String province,
    String city, List employees) {
    this.name = name;
    this.country = country;
    this.province = province;
    this.city = city;
    this.employees = employees;
    }
    }
    public 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 class Client {
    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 employees;
    public Department(String name, String country, String province,
    String city, List employees) {
    this.name = name;
    this.country = country;
    this.province = province;
    this.city = city;
    this.employees = employees;
    }
    }
    public 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 class Client {
    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 employees;
    public Department(String name, String country, String province,
    String city, List 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 emps = new ArrayList<>();
    for (int i = 0; i < department.employees.size(); i ++) {
    emps.add((Employee) employees.get(i).clone());
    }
    department.employees = emps;
    return department;
    }
    }
    public 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 class Client {
    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 employees;
    public Department(String name, String country, String province,
    String city, List employees) {
    this.name = name;
    this.country = country;
    this.province = province;
    this.city = city;
    this.employees = employees;
    }
    }
    public 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 Client {
    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方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。

    总结

    优点

    • 由于是直接从内存中读取对象进行克隆,所以性能卓越。

    • 代码量不论是相较于传统写法要精简很多,尤其是序列化与反序列化的方式。

    缺点

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

    适用场景

    • 适用于只有细微参数变动的对象创建。

    • 适用于需要备份的场景。如,当业务执行过程中,某种情况下需要数据回滚的时候,提前备份可以使用。

  • 相关阅读:
    【考研】数据结构考点——直接选择排序
    Selenium alert 弹窗处理!
    archlinux 上使用 nvm 来管理npm版本
    YOLO学习
    Java集合相关总结(List | Set | Map)
    LINUX 命令行合集
    计算机视觉40例之案例02人脸打码与解码
    基础架构之分布式任务
    Hive基础2
    6、JVM调优
  • 原文地址:https://www.cnblogs.com/buzuweiqi/p/16709147.html