一个应用工程里面,一遍会涉及到很多的模型转换,如DTO模型转DO模型,DO模型转DTO, 或者Request转DTO模型,总的来说,维护起来还是相对比较复杂。每涉及一个转换都需要重新写对应类的get或者set方法,并且这些方法散落在不同的模块里面,非常不方便管理。 下面介绍 转换器设计模式来解决上面这个问题。
在这篇文章中,会介绍 Converter Design Pattern。由于Java8 功能不仅提供了相应类型之间的通用双向转换方式,而且还提供了转换相同类型对象集合的常用方法,从而将样板代码减少到绝对最小值。
/**
* A converter converts a source object of type {@code S} to a target of type {@code T}.
*
* Implementations of this interface are thread-safe and can be shared.
*
*
Implementations may additionally implement {@link ConditionalConverter}.
*
* @author Keith Donald
* @since 3.0
* @param the source type
* @param the target type
*/
public interface Converter<S, T> {
/**
* Convert the source object of type {@code S} to target type {@code T}.
* @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
* @return the converted object, which must be an instance of {@code T} (potentially {@code null})
* @throws IllegalArgumentException if the source cannot be converted to the desired target type
*/
T convert(S source);
}
该接口为函数式接口,因此可以用lamda方式实现转换。这种简单方式本篇不再介绍。可以参考这篇文章(https://wenku.baidu.com/view/d64211654731b90d6c85ec3a87c24028915f859c.html?wkts=1693142368160&bdQuery=Converter+java),本篇这样介绍设计模式相关内容。
public interface MyConverter extends Converter {
/**
* 将DTO对象转换为领域对象
* @param dtoData 原模型
* @return 目标模型
*/
T convert(S dtoData);
/**
* 转换领域模型列表
* @param dtoDatas 原模型列表
* @return 目标模型列表
*/
List convert(List dtoDatas);
}
在使用上,一般先基于开始接口定位自己业务接口,这里满足了,单数据,或者列表数据。
然后写自己模版类,后面的具体模型转换器基于这个模版实现
public abstract class TemplateConverter<S, T> implements MyConverter<S, T> {
/** 实体sourceClass */
protected final Class<S> sourceClass;
/** targetClass */
protected final Class<T> targetClass;
/** 构造方法,约束泛型类型 */
public TemplateConverter() {
try {
ParameterizedType parameterizedType = ((ParameterizedType) getClass()
.getGenericSuperclass());
sourceClass = (Class<S>) parameterizedType.getActualTypeArguments()[0];
targetClass = (Class<T>) parameterizedType.getActualTypeArguments()[1];
} catch (Exception e) {
throw new RuntimeException("no definition");
}
}
/**
* 源模型 转 目标模型
* @param sourceModel 源模型
* @return 目标模
*/
public T convert(S sourceModel) {
// 空请求默认返回空
if (sourceModel == null) {
return null;
}
T domainModel;
try {
domainModel = targetClass.newInstance();
// 执行转换
excuteConvert(sourceModel, domainModel);
} catch (Exception e) {
StringBuilder bf = new StringBuilder("conversion error,source:");
bf.append(sourceClass.getSimpleName()).append(",target:")
.append(targetClass.getSimpleName());
throw new RuntimeException("convert RuntimeException");
}
return domainModel;
}
/**
* 源模型(List)转换为目标模型(List)
*
* @param sourceModels 源模型列表
* @return 目标模型列表
*/
public List<T> convert(List<S> sourceModels) {
// 空请求,默认返回空
if (CollectionUtils.isEmpty(sourceModels)) {
return null;
}
List<T> result = new ArrayList<>();
for (S dtoData : sourceModels) {
T resData = convert(dtoData);
if (resData != null) {
result.add(resData);
}
}
return result;
}
/**
* 执行具体的模型转换
* @param sourceModel 源模型
* @param targetModel 目标模型
*/
public abstract void excuteConvert(S sourceModel, T targetModel);
}
具体到模型转换器,这里还可以有很多个,这里以StudentModeConverter为例,只涉及到DTO模型转 DO模型
public class StudentModeConverter extends
TemplateConverter<StudentModeDTO, StudentModeDO> {
@Override
public void doConvert(StudentModeDTO sourceModel,
StudentModeDO targetModel) {
targetModel.setName(sourceModel.getName());
// 下面省略很多get/set
targetModel.setAge(sourceModel.getAge());
}
}
后面还可以写具体的转换器。基于之前模版。
public class CommonConversionServiceImpl extends GenericConversionService
{
/** constructor */
public CommonConversionServiceImpl() {
// 添加转换器
addDefaultConverters(this);
}
/**
* 添加转换器
* @param converterRegistry
*/
public void addDefaultConverters(ConverterRegistry converterRegistry) {
// 添加通用集合转换器
converterRegistry.addConverter(new StudentModeConverter1());
converterRegistry.addConverter(new StudentModeConverter2());
// ....
converterRegistry.addConverter(new StudentModeConverter3());
}
public class CommonConvertUtil {
/**
* 通用转换服务
*/
private static CommonConversionService conversionService = new CommonConversionServiceImpl();
/**
* 类型转换
* @param source
* @param targetType
* @param
* @return
*/
public static <T> T convert(Object source, Class<T> targetType) {
return conversionService.convert(source, targetType);
}
使用场景:
studentModeDTO 转 StudentModeDO
StudentModeDTO studentModeDTO = new StudentModeDTO();
StudentModeDO studentModeDO= CommonConvertUtil.convert(studentModeDTO, StudentModeDO.class);
通过调用该封装好的工具即可。
以后只需要在 CommonConversionServiceImpl 加具体转换器即可使用在CommonConvertUtil 中使用 。
当时用于 CommonConversionServiceImpl 是需要默认初始化,所有可以声明为工厂bean
public class CommonConversionServiceFactoryBean implements FactoryBean<CommonConversionService>,
InitializingBean {
/** 转换器定义 */
private Set<?> converters;
/** 通用转换服务 */
private CommonConversionService conversionService;
/**
* 注入转换器
* @param converters
*/
public void setConverters(Set<?> converters) {
this.converters = converters;
}
@Override
public CommonConversionService getObject() throws Exception {
return this.conversionService;
}
@Override
public Class<?> getObjectType() {
return GenericConversionService.class;
}
@Override
public boolean isSingleton() {
return false;
}
/**
* 创建转换服务
* @return
*/
protected CommonConversionService createConversionService() {
return new CommonConversionServiceImpl();
}
@Override
public void afterPropertiesSet() throws Exception {
this.conversionService = createConversionService();
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}
}