如何重构Java中大量使用instanceof的运行时类型转换代码以符合开闭原则
优化方案:基于转换注册表的泛型转换实现
核心思路是把「输入类型→输出类型」的转换逻辑从各输出类型的Converter中拆分出来,以输入类型+输出类型为唯一键统一注册到容器中,完全消除instanceof判断,同时符合开闭原则。
实现步骤
- 定义转换函数与注册表容器
import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import lombok.Value; // 通用转换函数接口 @FunctionalInterface interface ConversionFunc<IN extends FieldValue, OUT extends FieldValue> { OUT convert(IN input); } // 转换注册表全局容器 class ConversionRegistry { private static final Map<ClassPair, ConversionFunc<?, ?>> REGISTRY = new ConcurrentHashMap<>(); @Value(staticConstructor = "of") static class ClassPair { Class<? extends FieldValue> inType; Class<? extends FieldValue> outType; } // 注册转换规则 public static <IN extends FieldValue, OUT extends FieldValue> void register( Class<IN> inType, Class<OUT> outType, ConversionFunc<IN, OUT> func ) { REGISTRY.put(ClassPair.of(inType, outType), func); } // 查询转换规则 @SuppressWarnings("unchecked") public static <IN extends FieldValue, OUT extends FieldValue> Optional<ConversionFunc<IN, OUT>> getConverter( Class<IN> inType, Class<OUT> outType ) { return Optional.ofNullable((ConversionFunc<IN, OUT>) REGISTRY.get(ClassPair.of(inType, outType))); } }
- 项目启动时统一注册所有转换规则
import static java.lang.Integer.parseInt; import static java.lang.Float.parseFloat; import java.time.LocalDate; import java.time.ZonedDateTime; // 项目初始化阶段执行注册逻辑 static { // 转IntegerValue的规则 ConversionRegistry.register(IntegerValue.class, IntegerValue.class, val -> val); ConversionRegistry.register(FloatValue.class, IntegerValue.class, val -> IntegerValue.of(val.getValue().intValue())); ConversionRegistry.register(StringValue.class, IntegerValue.class, val -> IntegerValue.of(parseInt(val.getValue()))); // 转FloatValue的规则 ConversionRegistry.register(FloatValue.class, FloatValue.class, val -> val); ConversionRegistry.register(IntegerValue.class, FloatValue.class, val -> FloatValue.of(val.getValue().floatValue())); ConversionRegistry.register(StringValue.class, FloatValue.class, val -> FloatValue.of(parseFloat(val.getValue()))); // 转DateValue的规则 ConversionRegistry.register(DateValue.class, DateValue.class, val -> val); ConversionRegistry.register(StringValue.class, DateValue.class, val -> DateValue.of(LocalDate.parse(val.getValue()))); // 转TimestampValue的规则 ConversionRegistry.register(TimestampValue.class, TimestampValue.class, val -> val); ConversionRegistry.register(StringValue.class, TimestampValue.class, val -> TimestampValue.of(ZonedDateTime.parse(val.getValue()))); }
- 替换原有分散的Converter为通用泛型Converter
@Value(staticConstructor = "of") static class GenericConverter<T extends FieldValue> implements Converter<T> { Class<T> outType; @Override public T apply(FieldValue value) { return ConversionRegistry.getConverter(value.getClass(), outType) .map(func -> func.convert(value)) .orElseThrow(() -> new IllegalArgumentException( "Impossible to convert a " + value.getClass().getName() + " to a " + outType.getSimpleName() )); } }
- 调用逻辑调整
public static void main(String[] args) { // 运行时动态确定输入输出类型 final FieldValue value = StringValue.of("3.14"); final Converter<FloatValue> converter = GenericConverter.of(FloatValue.class); Mapper<FloatValue> mapper = Mapper.of(converter); FloatValue converted = mapper.convert(value); System.out.println(converted); }
方案优势
- 完全符合开闭原则:新增输入类型仅需补充对应注册规则,无需修改原有转换逻辑;新增输出类型仅需注册该类型对应的所有输入转换规则,完全不侵入旧代码
- 消除所有
instanceof判断和强制类型转换逻辑,转换规则统一收口管理,排查和修改转换逻辑无需遍历各个Converter实现类 - 扩展能力强:后续可灵活新增兜底转换逻辑,比如找不到精确匹配规则时自动尝试父类转换、字符串中间转换等,无需修改核心转换流程
内容的提问来源于stack exchange,提问作者CidTori
相关产品推荐
相关产品推荐

