java - 使用泛型实现对象转换器设计模式
问题描述
我正在尝试为我的 dto <-> 实体类创建一个通用对象转换器。我创建了一个抽象类,其中包含两个代表两种转换的函数,然后在我的具体转换器类中对其进行了扩展。
但是我想要一个通用的转换服务,我可以在启动期间注册我所有的转换器,然后方便地调用一个方法来处理彼此之间的转换。
这是我到目前为止提出的:
转换器抽象类
public abstract class Converter<D, E> {
private final Function<D, E> fromFirstToSecond;
private final Function<E, D> fromSecondToFirst;
public Converter(final Function<D, E> fromFirstToSecond, final Function<E, D> fromSecondToFirst) {
this.fromFirstToSecond = fromFirstToSecond;
this.fromSecondToFirst = fromSecondToFirst;
}
public final E convertFromFirstToSecond(final D first) {
return fromFirstToSecond.apply(first);
}
public final D convertFromSecondToFirst(final E second) {
return fromSecondToFirst.apply(second);
}
}
转换器具体类
public class MyConverter extends Converter<MyDTO, MyEntity> {
public OrderConverter() {
super(
MyConverter::fromDTOToEntity,
MyConverter::fromEntityToDTO
);
}
private static MyEntity fromDTOToEntity(MyDTO dto) {
return MyEntity.builder()
.field1(dto.getField1())
.field2(dto.getField2())
.build();
}
private static MyDTO fromEntityToDTO(MyEntity entity) {
return MyDTO.builder()
.field1(entity.getField1())
.field2(entity.getField2())
.build();
}
}
我想实现这样的目标(这是我需要帮助的地方):
@Configuration
public class ConverterConfiguration {
@Bean
public ConverterService converterService() {
ConverterService converterService = new ConverterService();
converterService.registerConverter(new MyConverter());
// Register any other converter...
return converterService;
}
}
该服务看起来像这样:
public class ConverterService {
private Map<Key, Converter<?, ?>> converters = new ConcurrentReferenceHashMap<>();
public void registerConverter(Converter<?, ?> converter) {
this.converterCache.put(key, converter);
}
protected <I, O> I mapBetweenTypes(final O from, final Class<I> clazz) {
// Based on the parameters I should be able to figure out which function to get from the map and then call (convertFromFirstToSecond or convertFromSecondToFirst)
// return this.converters.get(key).convertFromFirstToSecond(from);
return this.converters.get(key).convertFromSecondToFirst(from);
}
}
解决方案
解决该问题的一种方法是在类中显式存储属于转换一部分的Converter
类型。我会考虑的另一个简化是将转换函数直接存储在地图中,而不是转换器。然后你不必弄清楚你需要走哪条路。
第一步,存储类型,并为转换函数添加 getter:
public abstract class Converter<D, E> {
private final Function<D, E> fromFirstToSecond;
private final Function<E, D> fromSecondToFirst;
private final Class<D> classOfFirst;
private final Class<E> classOfSecond;
public Class<D> getClassOfFirst() {
return classOfFirst;
}
public Class<E> getClassOfSecond() {
return classOfSecond;
}
public Converter(Class<D> first, Class<E> second,
Function<D, E> fromFirstToSecond, Function<E, D> fromSecondToFirst) {
this.fromFirstToSecond = fromFirstToSecond;
this.fromSecondToFirst = fromSecondToFirst;
classOfFirst = first;
classOfSecond = second;
}
public Function<D, E> getForward() {
return fromFirstToSecond;
}
public Function<E, D> getBackward() {
return fromSecondToFirst;
}
然后ConverterService
可以存储两个方向的转换函数,并且转换相当于在两级映射中查找。
public class ConverterService {
private Map<Class<?>, Map<Class<?>, Function<?,?>>> converters = createMap();
public void registerConverter(Converter<?, ?> converter) {
if( !converters.containsKey(converter.getClassOfFirst())) {
converters.put(converter.getClassOfFirst(), createInnerMap());
}
if( !converters.containsKey(converter.getClassOfSecond())) {
converters.put(converter.getClassOfSecond(), createInnerMap());
}
converters.get(converter.getClassOfFirst()).put(
converter.getClassOfSecond(), converter.getForward());
converters.get(converter.getClassOfSecond()).put(
converter.getClassOfFirst(), converter.getBackward());
}
@SuppressWarnings("unchecked") // Also needs input validation
protected <D,E> E mapBetweenTypes(D from, E to) {
return ((Function<D,E>) converters.get(
from.getClass()).get(to.getClass())).apply(from);
}
private static Map<Class<?>, Map<Class<?>, Function<?,?>>> createMap() {
return /* Whatever impl you need */
}
private static Map<Class<?>, Function<?,?>> createInnerMap() {
return /* Whatever impl you need */
}
}
推荐阅读
- python-3.x - 如何在streamlit中删除标题前的额外间距?
- powershell - 将防火墙日志转换为 Powershell 哈希表
- python-3.x - 如何覆盖库类的方法
- python - AttributeError:模块“torch.fft”没有属性“fftfreq”
- sql - SQL使用他们的出生日期计算离生日的日子
- java - JSONObject 将 BigDecimal 转换为字符串
- python - Elasticsearch 探索数据
- r - 数据帧中逻辑值的逐行总和
- windows - 我需要访问 Windows 开始菜单中列出的所有程序
- javascript - 如何在 Next.js 公共目录中使用环境变量