如何减少对象字段更新校验中的if语句?
优化重复if校验的几种方案
你的需求是校验DTO与实体对象的字段差异并收集变更字段,当前大量重复if的写法确实冗余,以下是几种可行的优化方案:
方案1:函数式接口封装校验逻辑(推荐)
通过封装字段的获取逻辑和对应名称,把重复的校验逻辑抽象成通用规则,遍历执行即可。这种方式性能无损耗,可读性和维护性都很强。
/** * 检查公司信息是否有更新 * * @param company 从数据库获取的待更新公司数据 * @param companyDTO 可能包含更新数据的DTO * @return 包含变更字段名的列表 */ private List<String> getChanges(Company company, CompanyDTO companyDTO) { List<String> changes = new ArrayList<>(); // 定义所有字段的校验规则:DTO字段获取器、实体字段获取器、变更记录用的字段名 List<FieldCheckRule> checkRules = Arrays.asList( new FieldCheckRule(companyDTO::getName, company::getName, COMPANY_NAME), new FieldCheckRule(companyDTO::getShortName, company::getShortName, COMPANY_SHORTNAME), new FieldCheckRule(companyDTO::getAddress, company::getAddress, COMPANY_ADDRESS), new FieldCheckRule(companyDTO::getTaxNumber, company::getTaxNumber, COMPANY_TAX_NUMBER), new FieldCheckRule(companyDTO::getFirmId, company::getFirmId, COMPANY_FIRM_ID), new FieldCheckRule(companyDTO::getBankName, company::getBankName, COMPANY_BANK_NAME), new FieldCheckRule(companyDTO::getBankAccountNumber, company::getBankAccountNumber, COMPANY_BANK_ACCOUNT_NUMBER), new FieldCheckRule(companyDTO::getInternalComment, company::getInternalComment, COMPANY_INTERNAL_COMMENT), new FieldCheckRule(companyDTO::getEnabled, company::getEnabled, ENABLED) ); // 统一执行校验逻辑 for (FieldCheckRule rule : checkRules) { Object dtoValue = rule.dtoGetter.get(); if (dtoValue != null && !dtoValue.equals(rule.entityGetter.get())) { changes.add(rule.fieldName); } } return changes; } // 内部辅助类,封装单字段的校验规则 private static class FieldCheckRule { final Supplier<Object> dtoGetter; final Supplier<Object> entityGetter; final String fieldName; FieldCheckRule(Supplier<Object> dtoGetter, Supplier<Object> entityGetter, String fieldName) { this.dtoGetter = dtoGetter; this.entityGetter = entityGetter; this.fieldName = fieldName; } }
方案2:反射遍历字段(适合字段极多的场景)
如果字段数量非常多,不想逐个编写getter,可以用反射自动遍历字段。但反射会有一定性能开销,且需要处理字段名映射和异常。
private List<String> getChanges(Company company, CompanyDTO companyDTO) throws IllegalAccessException { List<String> changes = new ArrayList<>(); // 定义DTO字段名 -> 实体字段名 -> 变更记录字段名的映射 Map<String, Map.Entry<String, String>> fieldMapping = new HashMap<>(); fieldMapping.put("name", new AbstractMap.SimpleEntry<>("name", COMPANY_NAME)); fieldMapping.put("shortName", new AbstractMap.SimpleEntry<>("shortName", COMPANY_SHORTNAME)); fieldMapping.put("address", new AbstractMap.SimpleEntry<>("address", COMPANY_ADDRESS)); // 其他字段依次添加... for (Map.Entry<String, Map.Entry<String, String>> entry : fieldMapping.entrySet()) { String dtoFieldName = entry.getKey(); String entityFieldName = entry.getValue().getKey(); String changeFieldName = entry.getValue().getValue(); // 获取DTO字段值 Field dtoField = CompanyDTO.class.getDeclaredField(dtoFieldName); dtoField.setAccessible(true); Object dtoValue = dtoField.get(companyDTO); if (dtoValue != null) { // 获取实体字段值并对比 Field entityField = Company.class.getDeclaredField(entityFieldName); entityField.setAccessible(true); Object entityValue = entityField.get(company); if (!dtoValue.equals(entityValue)) { changes.add(changeFieldName); } } } return changes; }
方案3:借助第三方对象映射库
如果你的项目已经在使用ModelMapper、MapStruct这类对象映射库,可以扩展它们的功能来收集变更字段。比如ModelMapper可以通过自定义转换器记录差异,这种方式适合同时需要对象映射和变更记录的场景,避免重复开发。
方案选择建议
- 字段数量适中时,优先用方案1:代码清晰、性能好,新增字段只需加一行规则,维护成本低。
- 字段数量极多且维护成本优先时,考虑方案2:减少重复代码,但要注意反射的性能和异常处理。
- 已有对象映射需求时,用方案3:复用现有依赖,避免重复造轮子。
内容的提问来源于stack exchange,提问作者Aron.jt
相关产品推荐
相关产品推荐

