多类型Disposition类导出映射switch-case逻辑重构方案咨询
重构方案
针对当前分表实体转导出结构的场景,不需要调整底层库表,以下两种方案都可以解决switch分支膨胀、维护成本高的问题:
方案一:策略模式+统一字段提取接口(推荐)
这个方案完全不侵入现有实体类和数据库结构,将散落在各个switch分支的同类型逻辑收拢,符合开闭原则,后续新增类型只需要加对应实现即可。
- 首先定义统一的详情字段提取接口,统一所有导出字段的语义约定,不用关心不同详情类的字段名差异:
import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; /** * 不同类型 disposition 详情的导出字段提取器 */ public interface DispositionDetailExporter { // 匹配当前提取器对应的业务类型 DispositioType matchType(); // 以下为所有导出需要的字段,默认返回null,无对应字段的类型不需要重写 default String getLocalBankAccount(Disposition disposition) {return null;} default boolean getIsForeignBankAccount(Disposition disposition) {return false;} default String getForeignBankAccount(Disposition disposition) {return null;} default String getForeignBankSwift(Disposition disposition) {return null;} default String getDepartmentName(Disposition disposition) {return null;} default String getCustomerName(Disposition disposition) {return null;} default String getDocumentNumber(Disposition disposition) {return null;} default String getDocumentTitle(Disposition disposition) {return null;} default String getDocumentDescription(Disposition disposition) {return null;} }
- 为每个业务类型写对应的实现类,把原来switch里的逻辑全部收拢到对应实现中,比如A类型、C类型(包含isExportable特殊判断)、D类型的实现示例:
// A类型详情提取逻辑 @Component public class TypeADetailExporter implements DispositionDetailExporter { @Override public DispositioType matchType() { return DispositioType.DispositionA; } @Override public String getLocalBankAccount(Disposition disposition) { return disposition.getDispostionDetailsA().getLocalBankAccount(); } @Override public boolean getIsForeignBankAccount(Disposition disposition) { return disposition.getDispostionDetailsA().isForeignBankAccount(); } @Override public String getForeignBankAccount(Disposition disposition) { return disposition.getDispostionDetailsA().getForeignBankAccount(); } @Override public String getForeignBankSwift(Disposition disposition) { return disposition.getDispostionDetailsA().getForeignBankSwift(); } @Override public String getDepartmentName(Disposition disposition) { return disposition.getDispostionDetailsA().getDepartmentName(); } }
// C类型详情提取逻辑,包含isExportable特殊判断 @Component public class TypeCDetailExporter implements DispositionDetailExporter { @Override public DispositioType matchType() { return DispositioType.DispositionC; } @Override public String getLocalBankAccount(Disposition disposition) { DispositionDetailsC details = disposition.getDispostionDetailsC(); return details.isExportable() ? details.getLocalBankAccount() : null; } @Override public boolean getIsForeignBankAccount(Disposition disposition) { DispositionDetailsC details = disposition.getDispostionDetailsC(); return details.isExportable() && details.isForeignBankAccount(); } @Override public String getForeignBankAccount(Disposition disposition) { DispositionDetailsC details = disposition.getDispostionDetailsC(); return details.isExportable() ? details.getForeignBankAccount() : null; } @Override public String getForeignBankSwift(Disposition disposition) { DispositionDetailsC details = disposition.getDispostionDetailsC(); return details.isExportable() ? details.getForeignBankSwift() : null; } @Override public String getDocumentNumber(Disposition disposition) { DispositionDetailsC details = disposition.getDispostionDetailsC(); return details.isExportable() ? details.getDocumentNumber() : null; } @Override public String getDocumentTitle(Disposition disposition) { DispositionDetailsC details = disposition.getDispostionDetailsC(); return details.isExportable() ? details.getDocumentTitle() : null; } }
// D类型详情提取逻辑,无银行类字段直接用接口默认返回值 @Component public class TypeDDetailExporter implements DispositionDetailExporter { @Override public DispositioType matchType() { return DispositioType.DispositionD; } @Override public String getDepartmentName(Disposition disposition) { return disposition.getDispostionDetailsD().getDepartmentName(); } @Override public String getCustomerName(Disposition disposition) { return disposition.getDispostionDetailsD().getCustomerName(); } @Override public String getDocumentDescription(Disposition disposition) { return disposition.getDispostionDetailsD().getDocumentDescription(); } }
- 改造原有Exporter类,启动时自动收集所有提取器实例,构建类型和提取器的映射,完全移除switch逻辑:
@Component public class Exporter { private final Map<DispositioType, DispositionDetailExporter> exporterMap; // 如果使用Spring容器,会自动注入所有DispositionDetailExporter的实现类 public Exporter(List<DispositionDetailExporter> exporterList) { this.exporterMap = exporterList.stream() .collect(Collectors.toMap(DispositionDetailExporter::matchType, Function.identity())); } public ExportDataStructure exportDisposition(Disposition disposition){ ExportDataStructure export = new ExportDataStructure(); export.setId(disposition.getId()); DispositionDetailExporter detailExporter = exporterMap.get(disposition.getDispositionType()); if (detailExporter == null) { return export; } // 统一赋值,无需每个字段写分支判断 export.setLocalBankAccount(detailExporter.getLocalBankAccount(disposition)); export.setIsForeignBankAccount(detailExporter.getIsForeignBankAccount(disposition)); export.setForeignBankAccount(detailExporter.getForeignBankAccount(disposition)); export.setForeignBankSwift(detailExporter.getForeignBankSwift(disposition)); export.setDepartmentName(detailExporter.getDepartmentName(disposition)); export.setCustomerName(detailExporter.getCustomerName(disposition)); export.setDocumentNumber(detailExporter.getDocumentNumber(disposition)); export.setDocumentTitle(detailExporter.getDocumentTitle(disposition)); export.setDocumentDescription(detailExporter.getDocumentDescription(disposition)); return export; } }
这个方案的优势:
- 零库表改造,不侵入原有实体类代码,对生产环境无影响
- 同类型的所有取值逻辑收拢在单个类中,不会散落在各个字段的switch分支里,排查问题只需要找对应类型的实现类
- 新增业务类型时只需要新增对应的提取器实现类,不需要修改Exporter核心逻辑,不会因为加类型漏改某个字段的switch分支
- 完美适配字段名不一致、存在特殊取值规则(比如C类型的isExportable判断)的场景,接口层统一语义,实现层做自定义映射
方案二:字段映射配置+反射取值(轻量可选)
如果不想写过多实现类,可以把每个类型的字段映射关系存在枚举或配置文件中,通过反射统一取值赋值:
- 定义映射枚举,存储每个类型对应的详情字段名、导出字段和详情字段的对应关系,以及特殊校验规则:
import java.util.Map; import java.util.function.Predicate; @Getter public enum DispositionFieldMapping { A(DispositioType.DispositionA, "dispositionDetailsA", Map.of("localBankAccount","localBankAccount", "isForeignBankAccount","isForeignBankAccount", "foreignBankAccount","foreignBankAccount", "foreignBankSwift","foreignBankSwift", "departmentName","departmentName"), details -> true ), C(DispositioType.DispositionC, "dispositionDetailsC", Map.of("localBankAccount","localBankAccount", "isForeignBankAccount","isForeignBankAccount", "foreignBankAccount","foreignBankAccount", "foreignBankSwift","foreignBankSwift", "documentNumber","documentNumber", "documentTitle","documentTitle"), details -> ((DispositionDetailsC)details).isExportable() ), D(DispositioType.DispositionD, "dispositionDetailsD", Map.of("departmentName","departmentName", "customerName","customerName", "documentDescription","documentDescription"), details -> true ); private final DispositioType type; // Disposition类中存储对应详情的字段名 private final String detailsFieldName; // key:ExportDataStructure的字段名,value:对应详情类的字段名 private final Map<String, String> exportFieldToDetailField; // 导出前置校验,比如C类型的isExportable判断 private final Predicate<Object> exportCheck; DispositionFieldMapping(DispositioType type, String detailsFieldName, Map<String, String> exportFieldToDetailField, Predicate<Object> exportCheck) { this.type = type; this.detailsFieldName = detailsFieldName; this.exportFieldToDetailField = exportFieldToDetailField; this.exportCheck = exportCheck; } }
- 写通用的反射转换逻辑,根据映射关系自动取值赋值,不需要写任何分支判断。这个方案代码量更少,但反射存在极轻微的性能损耗,复杂逻辑的可读性比方案一差,适合业务规则简单、类型变动频繁的场景。
- 注意:你提供的示例代码中重复定义了
DispositionDetailsC类,第二个类实际应为DispositionDetailsD,实现时注意修正笔误。
内容的提问来源于stack exchange,提问作者patry6123
相关产品推荐
相关产品推荐

