转换器(Converter)设计模式
在日常开发的时候,需要在对象之间进行值的 copy,如 POJO,DTO,VO,对象之间有相同的属性,想把一个对象的值 copy 到另一个对象中去,如 从数据库中查询出我们的 POJO 对象的数据,又有个对象是对 POJO 进行包装DTO,现在想把查询出来的 POJO 的值 copy 到 DTO 中相应的属性中去,之后再扩展其属性,对此,一般可以有三种方式进行解决:setter,转换器模式和反射,接下来就看下它们的一个区别:
在区分这三种方式之前,先要定义一下需要进行数据copy的两个类:
Person类:
@Getter @Setter @NoArgsConstructor @AllArgsConstructor @ToString public class Person { private String name; private int age; private String gender; private String job; }
PersonDto类:
@Getter @Setter @NoArgsConstructor @AllArgsConstructor @ToString public class PersonDto { private String name; private int age; private String gender; private String address; }
下面的演示都是用这两个类进行。
setter
通过 setter 方式 copy 数据比较方便,但是如果在很多地方需要进行数据的 copy,就显得有点重复了,当然可以写个工具,专门对该数据进行扩展,如下所示:
public Person copy(PersonDto personDto) { Person person = new Person(); person.setName(personDto.getName()); person.setAge(personDto.getAge()); person.setGender(personDto.getGender()); return person; }
使用该方式比较简单。
转换器(Converter)模式
接下来就到该文章的主题了,可以使用转换器模式来解决该问题,先看下该模式的一个类图:
首先 Converter 类是一个顶层的接口,定义了公共的转换方法,不同类实现该接口来定义自己的转换规则
PersonConverter 类是继承于 Converter 的,定义了从 Person 到 PersonDto 和从 PersonDto 到 Person 的一个转换规则,并向外提供接口以供使用。接下来看下 顶层接口 Converter 类的定义:
/** * 定义转换器 * @ Date:Created in 下午 4:44 2018/9/27 0027 */ public class Converter<T, U> { // 从 T 转换为 U private Function<T, U> fromDto; // 从 U 转换为 T private Function<U, T> fromEntity; public Converter(final Function<T, U> fromDto, final Function<U, T> fromEntity) { this.fromDto = fromDto; this.fromEntity = fromEntity; } public final U converterFromDto(final T dto){ return fromDto.apply(dto); } public final T converterFromEntity(final U entity){ return fromEntity.apply(entity); } public final List<U> batchConverterFromDto(final List<T> dtos){ return dtos.stream().map(this::converterFromDto).collect(Collectors.toList()); } public final List<T> batchConverterFromEntity(final List<U> entities){ return entities.stream().map(this::converterFromEntity).collect(Collectors.toList()); } }
然后在看看 Person 类的自定义转换规则,PersonConverter类:
/** * Person 转换器 * @ Date:Created in 下午 5:00 2018/9/27 0027 */ public class PersonConverter extends Converter<PersonDto, Person> { public PersonConverter() { super(new PersonDtoFunction(), new PersonFunction()); } // 自定义转换规则 static class PersonDtoFunction implements Function<PersonDto, Person> { @Override public Person apply(PersonDto personDto) { // 可定制需要复制的属性 Person person = new Person(); person.setName(personDto.getName()); person.setAge(personDto.getAge()); person.setGender(personDto.getGender()); return person; } } // 自定义转换规则 static class PersonFunction implements Function<Person, PersonDto> { @Override public PersonDto apply(Person person) { // 可定制需要复制的属性 PersonDto dto = new PersonDto(); dto.setName(person.getName()); dto.setAge(person.getAge()); dto.setGender(person.getGender()); return dto; } } }
接下来进行测试一番:
PersonDto 转换为 Person:
Converter<PersonDto, Person> converter = new PersonConverter(); PersonDto personDto = new PersonDto("zhangsan", 23, "male", "chengdou"); Person person = converter.converterFromDto(personDto); System.out.println(person); // Person(name=zhangsan, age=23, gender=male, job=null)
批量 PersonDto 转换为 Person:
PersonDto pd1 = new PersonDto("AAA", 20, "male", "beijing"); PersonDto pd2 = new PersonDto("BBB", 21, "female", "shanghai"); PersonDto pd3 = new PersonDto("CCC", 22, "male", "chengdou"); List<PersonDto> dtos = Lists.newArrayList(pd1, pd2, pd3); List<Person> persons = converter.batchConverterFromDto(dtos); persons.forEach((x) -> System.out.println(x)); 结果: Person(name=AAA, age=20, gender=male, job=null) Person(name=BBB, age=21, gender=female, job=null) Person(name=CCC, age=22, gender=male, job=null)
从 Perosn 转换为 PersonDto:
Person person1 = new Person("lisi", 25, "female", "java"); PersonDto personDto1 = converter.converterFromEntity(person1); System.out.println(personDto1); // PersonDto(name=lisi, age=25, gender=female, address=null)
批量 从 Perosn 转换为 PersonDto:
Person p1 = new Person("DDD", 25, "male", "java"); Person p2 = new Person("EEE", 26, "male", "python"); Person p3 = new Person("FFF", 27, "female", "C++"); List<Person> persons1 = Lists.newArrayList(p1, p2, p3); List<PersonDto> dtos1 = converter.batchConverterFromEntity(persons1); dtos1.forEach((x) -> System.out.println(x)); 结果: PersonDto(name=DDD, age=25, gender=male, address=null) PersonDto(name=EEE, age=26, gender=male, address=null) PersonDto(name=FFF, age=27, gender=female, address=null)
以上就是转换器模式的内容了,还是很好扩展的,不过,每个类需要自己定义一个转换规则,这个和写一个该类的转换工具方法有什么区别哦??
反射
第三种 copy 数据的方法就是反射了,使用反射后,可以复制所有的类的数据,不用每个类专门写工具方法和转换器了,如下所示:
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * * @ Date:Created in 上午 9:27 2018/9/28 0028 */ public final class BeanDataConverter { public static void converterData(Object fromBean, Object toBean, String[] excludeProperties) throws InvocationTargetException, IllegalAccessException { Objects.requireNonNull(fromBean); Objects.requireNonNull(toBean); Objects.requireNonNull(excludeProperties); List<String> excludes = Arrays.stream(excludeProperties).map(String::toLowerCase).collect(Collectors.toList()); Method[] methods = fromBean.getClass().getMethods(); for (Method method : methods) { String methodName = method.getName(); if (!methodName.startsWith("get") || "getClass".equals(methodName) || excludes.contains(methodName.replaceFirst("get", "").toLowerCase())){ continue; } Class<?> returnType = method.getReturnType(); Object value = method.invoke(fromBean, new Object[]{ }); String setMethodName = String.format("set%s", methodName.replaceFirst("get", "")); try { Method setMethod = toBean.getClass().getMethod(setMethodName, returnType); setMethod.invoke(toBean, value); } catch (NoSuchMethodException e) { } } } }
测试一波:
PersonDto personDto = new PersonDto("zhangsan", 23, "male", "chengdou"); Person p4 = new Person(); // 不排除属性,复制全部属性 String[] excludes = {}; BeanDataConverter.converterData(personDto, p4, excludes); System.out.println(p4); // Person(name=zhangsan, age=23, gender=male, job=null) Person p5 = new Person(); // 排除 name 属性,即不复制 name 属性的值: String[] excludes2 = {"name"}; BeanDataConverter.converterData(personDto, p5, excludes2); System.out.println(p5); // Person(name=null, age=23, gender=male, job=null)
以上就是使用 反射来进行copy数据了,可以看到反射是很强大的,适用于所有的数据copy,个人感觉比 转换模式要好些呢。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Go goroutine调度
前言 Goroutine调度是一个很复杂的机制,尽管Go源码中提供了大量的注释,但对其原理没有一个好的理解的情况下去读源码收获不会很大。下面尝试用简单的语言描述一下Goroutine调度机制,在此基础上再去研读源码效果可能更好一些。 1. 线程池的缺陷 我们知道,在高并发应用中频繁创建线程会造成不必要的开销,所以有了线程池。线程池中预先保存一定数量的线程,而新任务将不再以创建线程的方式去执行,而是将任务发布到任务队列,线程池中的线程不断的从任务队列中取出任务并执行,可以有效的减少线程创建和销毁所带来的开销。 下图展示一个典型的线程池: 为了方便下面的叙述,我们把任务队列中的每一个任务称作G,而G往往代表一个函数。 线程池中的线程worker线程不断的从任务队列中取出任务并执行。而worker线程的调度则交给操作系统进行调度。 如果worker线程执行的G任务中发生系统调用,则操作系统会将该线程置为阻塞状态,也意味着该线程在怠工,也意味着消费任务队列的worker线程变少了,也就是说线程池消费任务队列的能力变弱了。 如果任务队列中的大部分任务都会进行系统调用,则会让这种状态恶化,大部分w...
- 下一篇
Zabbix 通过 jmx 监控 tomcat
1. 安装 jdk 和 zabbix-java-gateway # 安装 openjdk 或者下载 tar.gz apt install openjdk-8-jdk # 安装 zabbix-java-gateway apt install zabbix-java-gateway 2. 修改服务器端配置 vim /etc/zabbix/zabbix_server.conf JavaGateway=192.168.6.30 JavaGatewayPort=10052 StartJavaPollers=50 systemctl restart zabbix-server systemctl start zabbix-java-gateway systemctl enable zabbix-java-gateway 3. 配置被监控 tomcat vim /tomcat/bin/catalina.sh CATALINA_OPTS="-Djava.rmi.server.hostname=< 被监控 tomcat 主机 IP 地址 > -Djavax.management.builde...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS关闭SELinux安全模块
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题