原 荐 转换器(Converter)设计模式

栏目: 后端 · 发布时间: 6年前

内容简介:在日常开发的时候,需要在对象之间进行值的 copy,如 POJO,DTO,VO,对象之间有相同的属性,想把一个对象的值 copy 到另一个对象中去,如 从数据库中查询出我们的 POJO 对象的数据,又有个对象是对 POJO 进行包装DTO,现在想把查询出来的 POJO 的值 copy 到 DTO 中相应的属性中去,之后再扩展其属性,对此,一般可以有三种方式进行解决:setter,转换器模式和反射,接下来就看下它们的一个区别:在区分这三种方式之前,先要定义一下需要进行数据copy的两个类:Person类:

在日常开发的时候,需要在对象之间进行值的 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)设计模式

首先 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,个人感觉比 转换模式要好些呢。


以上所述就是小编给大家介绍的《原 荐 转换器(Converter)设计模式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Beginning ARKit for iPhone and iPad

Beginning ARKit for iPhone and iPad

Wallace Wang / Apress / 2018-11-5 / USD 39.99

Explore how to use ARKit to create iOS apps and learn the basics of augmented reality while diving into ARKit specific topics. This book reveals how augmented reality allows you to view the screen on ......一起来看看 《Beginning ARKit for iPhone and iPad》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具