Java库开发:双向模型转换的设计模式选型问题
带外部依赖的双向转换场景的实现实践
现有实现的核心局限
当前实现的TypeConverter将转换逻辑绑定为无外部上下文的Function<待转换对象, 目标对象>,这种结构无法支撑转换流程中需要调用外部服务、嵌套转换器、读取配置等依赖场景。
之前评估的适配器模式、外观模式均不匹配当前需求:
- 适配器模式核心作用是兼容接口不兼容的现有类,没有解决转换逻辑的依赖承载问题
- 外观模式核心作用是给复杂子系统提供简化的高层调用入口,不涉及转换流程的依赖注入能力
注:原有代码中反向转换方法名
convertFromJspritType属于历史遗留笔误,对应泛型参数为OtherLibType,建议统一命名为convertFromOtherType,避免使用时产生歧义。
具体落地的3种可选实践
1. 上下文对象模式(最通用,适配动态依赖场景)
给转换流程增加统一的依赖载体,把原本无上下文的Function替换为带上下文的转换接口,所有转换需要的外部依赖都从上下文中获取。
首先定义统一的转换上下文,承载所有可能用到的依赖:
import java.util.Optional; // 转换上下文:存放转换流程需要的所有外部依赖 public class ConvertContext { // 示例依赖:嵌套类型的转换器注册表、配置项、外部服务等 private final Optional<ConverterRegistry> registry; private final Optional<MyConfig> config; public static final ConvertContext EMPTY = new ConvertContext(Optional.empty(), Optional.empty()); public ConvertContext(Optional<ConverterRegistry> registry, Optional<MyConfig> config) { this.registry = registry; this.config = config; } // getter方法省略 }
改造原有抽象转换器,兼容无依赖的简化调用:
import java.util.function.BiFunction; public abstract class TypeConverter<MyType, OtherLibType> { // 转换逻辑调整为接收【待转换对象 + 转换上下文】 private final BiFunction<MyType, ConvertContext, OtherLibType> fromMyType; private final BiFunction<OtherLibType, ConvertContext, MyType> fromOtherLibType; public TypeConverter(BiFunction<MyType, ConvertContext, OtherLibType> fromMyType, BiFunction<OtherLibType, ConvertContext, MyType> fromOtherLibType) { this.fromMyType = fromMyType; this.fromOtherLibType = fromOtherLibType; } // 全量方法:调用方传入上下文使用依赖 public final OtherLibType convertFromMyType(MyType myType, ConvertContext context) { return fromMyType.apply(myType, context); } public final MyType convertFromOtherType(OtherLibType otherLibType, ConvertContext context) { return fromOtherLibType.apply(otherLibType, context); } // 简化重载:不需要依赖的场景直接用空上下文调用,兼容原有使用方式 public final OtherLibType convertFromMyType(MyType myType) { return convertFromMyType(myType, ConvertContext.EMPTY); } }
这个方案的优势是无需改动无依赖的现有转换器,需要依赖的转换逻辑直接从上下文取所需对象即可,同时支持不同调用场景传入不同的依赖配置。
2. 工厂模式(适配固定全局依赖场景)
如果转换用到的依赖是应用启动时就确定的全局固定依赖(比如容器管理的服务实例),直接用工厂方法绑定依赖,生成对应的转换器实例即可,不需要额外改抽象类结构。
示例代码:
// 转换器工厂:持有全局固定依赖,生成对应转换器 public class TypeConverterFactory { // 全局固定依赖:比如用户服务、配置实例等 private final UserService userService; public TypeConverterFactory(UserService userService) { this.userService = userService; } // 生成绑定了UserService的订单转换器 public TypeConverter<MyOrder, OtherOrder> createOrderConverter() { return new TypeConverter<MyOrder, OtherOrder>( // 正向转换逻辑:直接使用注入的userService补全用户信息 (myOrder, context) -> { OtherUser orderUser = userService.getUserById(myOrder.getUserId()) .map(u -> new OtherUser(u.getId(), u.getName())) .orElseThrow(() -> new IllegalArgumentException("用户不存在")); return new OtherOrder(myOrder.getId(), orderUser, myOrder.getAmount()); }, // 反向转换逻辑不需要依赖的话直接忽略context参数 (otherOrder, context) -> new MyOrder( otherOrder.getId(), otherOrder.getUser().getId(), otherOrder.getAmount() ) ); } }
3. 转换器注册表模式(适配复杂嵌套转换场景)
如果你的转换逻辑存在多层嵌套(比如MyOrder里嵌套MyAddress、MyUser等字段,需要逐层调用对应的转换器),可以维护一个全局的转换器注册表,转换器内部需要调用其他转换器时直接从注册表查找,不需要在构造时手动传入所有嵌套依赖,降低耦合。
示例代码:
import java.util.HashMap; import java.util.Map; // 转换器注册表:存储所有已注册的类型对与转换器的映射 public class ConverterRegistry { private final Map<ClassPair, TypeConverter<?, ?>> converterMap = new HashMap<>(); // 注册转换器 public <MyType, OtherType> void register(Class<MyType> myTypeCls, Class<OtherType> otherTypeCls, TypeConverter<MyType, OtherType> converter) { converterMap.put(new ClassPair(myTypeCls, otherTypeCls), converter); } // 查找对应类型的转换器 @SuppressWarnings("unchecked") public <MyType, OtherType> TypeConverter<MyType, OtherType> getConverter(Class<MyType> myTypeCls, Class<OtherType> otherTypeCls) { return (TypeConverter<MyType, OtherType>) converterMap.get(new ClassPair(myTypeCls, otherTypeCls)); } // 类型对标识:用两个待映射的类作为唯一键 private record ClassPair(Class<?> first, Class<?> second) {} }
内容的提问来源于stack exchange,提问作者Nane Petrosyan
相关产品推荐
相关产品推荐

