首页 > 其他分享 >设计模式---原型模式

设计模式---原型模式

时间:2022-09-22 00:55:06浏览次数:75  
标签:province city String country private --- 原型 设计模式 name

简述

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

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

优化案例

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

原版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方法克隆对象。让问题在编译的时候暴露出来总是优于把问题留到运行时。

总结

优点

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

缺点

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

适用场景

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

标签:province,city,String,country,private,---,原型,设计模式,name
From: https://www.cnblogs.com/buzuweiqi/p/16709147.html

相关文章

  • day02-代码实现01
    多用户即时通讯系统024.编码实现014.1功能实现-用户登录4.1.1功能说明因为还没有学习数据库,我们人为规定用户名/id=100,密码为123456就可以登录,其他用户不能登录,后......
  • vue3 基础-动态组件 & 异步组件
    之前学习的都是父子组件传值的话题,一句话总结就是,常规数据通过属性传,dom结构通过插槽slot来传.而本篇则关注如何通过数据去控制组件的显示问题,如咱经常用到的......
  • python-mysql 批量造数据
    importpymysqldb=pymysql.connect(host="124.70.xxx.xxx",user="root",password="3xxxx",database="novel")mycursor=db.cursor()phone=......
  • Codeforces Round #813 (Div. 2) - D. Empty Graph
    构造Problem-D-Codeforces题意给\(n(1<=n<=10^5)\)个点,与权值\(a_i\),这\(n\)个点组成一个完全图,\(a_l\)与\(a_r\)连的边的权值为\(min(a_l,a_{l+1}...a_{r......
  • C#-01 关于C#中传入参数的一些用法
    实验环境  实验所处环境位于vs2019环境中学习内容一、最基础的参数传入:值参数对于这种传入,和其他的c,c++编程语言参数传入一样,没有太大差别,在这里给如下例子:虽然这......
  • Python之numpy库(二)-函数
    1.算术函数  如果参与运算的两个对象都是ndarray,并且形状相同,那么会对位彼此之间进行(+-*/)运算。NumPy算术函数包含简单的加减乘除:add(),subtract(),multiply()......
  • 前端面试总结04-作用域与闭包
    作用域:全局作用域函数作用域块级作用域(es6新增)自由变量:1.一个变量在当前作用域没有定义,但被使用了2.向上级作用域,一层一层依次寻找,直到找到为止3.如果到全局作用域......
  • 题解 P7839 「Wdoi-3」夜雀 singing (思路非常好的一道题)
    代码细节非常多的一道题。这里只说思想了先。首先,找到那些安全树。所有的乌鸦最后一定会到达某一棵安全树上。因此,对于每只乌鸦,分别向左和向右暴力寻找,看是否可到达安全......
  • Python commandline-config简洁命令行配置工具: 一个供用户以Python Dict或JSON格式编
    本文介绍了一个可以直接用pip安装的python工具包commandline-config,适合经常写python代码跑实验的研究生们,工具可以通过Python原生字典dict(支持嵌套)的形式来写实验的参数配......
  • Panama-FFI实现原理与移植
    移植FFI在说明如何对FFI进行移植之前需要先说明FFI的实现原理。JEP424是外部函数访问+本地内存,但是实际上需要移植的内容只有外部函数访问,对于本地内存的操作并不需要修改......