You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何重构多if语句以降低认知复杂度(SonarQube合规)

重构SonarQube认知复杂度超标的多if校验代码

问题背景

现有一段校验代码因大量重复if判断导致SonarQube认知复杂度超标,代码通过多个if判断body对象的各字段是否非空,若非空则调用rejectValueIf()方法校验字段大小是否超过配置的批量搜索上限。原代码示例如下:

if (body.getCprNumber() != null)
    rejectValueIf(
        body.getCprNumber().size() > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.CPR_NUMBER,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

if (body.getCvrNumber() != null)
    rejectValueIf(
        body.getCvrNumber().size() > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.CVR_NUMBER,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

if (body.getId() != null)
    rejectValueIf(
        body.getId().size() > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.ID,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

if (body.getSize() != null)
    rejectValueIf(
        body.getSize() > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.SIZE,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

if (body.getNext() != null)
    rejectValueIf(
        body.getNext().length() > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.NEXT,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

if (body.getPage() != null)
    rejectValueIf(
        body.getPage() > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.PAGE,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

if (body.getSortFields() != null)
    rejectValueIf(
        body.getSortFields().size() > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.SORT_FIELDS,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

if (body.getFields() != null)
    rejectValueIf(
        body.getFields().size() > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.FIELDS_CONSTANT,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

if (body.getSearchParameters() != null)
    rejectValueIf(
        body.getSearchParameters().size()
            > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.SEARCH_PARAMETERS,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

if (body.getLimitSearchToFields() != null)
    rejectValueIf(
        body.getLimitSearchToFields().size()
            > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.LIMIT_SEARCH_TO_FIELDS,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

if (body.getMaxSize() != null)
    rejectValueIf(
        body.getMaxSize() > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.MAX_SIZE,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

if (body.getDefaultSize() != null)
    rejectValueIf(
        body.getDefaultSize() > contactIndexConfigurationProperties.getBulkSearchLimit(),
        errors,
        ValidationConstants.Fields.DEFAULT_SIZE,
        ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED);

最优重构方案

方案1:字段校验元数据列表+统一遍历处理

通过封装字段的获取逻辑、大小计算逻辑和字段名称到元数据对象,再统一遍历处理,彻底消除重复if,大幅降低认知复杂度。

步骤1:定义校验元数据记录(Java 16+)

// 用记录封装每个字段的校验元数据
private record FieldValidator<T>(
    Supplier<T> valueSupplier, // 字段值获取器
    Function<T, Integer> sizeExtractor, // 字段大小计算逻辑
    String fieldName // 校验错误对应的字段名
) {}

步骤2:构建校验元数据列表

// 批量创建所有字段的校验元数据
List<FieldValidator<?>> validators = List.of(
    new FieldValidator<>(body::getCprNumber, Collection::size, ValidationConstants.Fields.CPR_NUMBER),
    new FieldValidator<>(body::getCvrNumber, Collection::size, ValidationConstants.Fields.CVR_NUMBER),
    new FieldValidator<>(body::getId, Collection::size, ValidationConstants.Fields.ID),
    new FieldValidator<>(body::getSize, size -> size, ValidationConstants.Fields.SIZE),
    new FieldValidator<>(body::getNext, String::length, ValidationConstants.Fields.NEXT),
    new FieldValidator<>(body::getPage, page -> page, ValidationConstants.Fields.PAGE),
    new FieldValidator<>(body::getSortFields, Collection::size, ValidationConstants.Fields.SORT_FIELDS),
    new FieldValidator<>(body::getFields, Collection::size, ValidationConstants.Fields.FIELDS_CONSTANT),
    new FieldValidator<>(body::getSearchParameters, Collection::size, ValidationConstants.Fields.SEARCH_PARAMETERS),
    new FieldValidator<>(body::getLimitSearchToFields, Collection::size, ValidationConstants.Fields.LIMIT_SEARCH_TO_FIELDS),
    new FieldValidator<>(body::getMaxSize, maxSize -> maxSize, ValidationConstants.Fields.MAX_SIZE),
    new FieldValidator<>(body::getDefaultSize, defaultSize -> defaultSize, ValidationConstants.Fields.DEFAULT_SIZE)
);

步骤3:统一遍历处理校验

// 批量执行所有字段的校验逻辑
int bulkSearchLimit = contactIndexConfigurationProperties.getBulkSearchLimit();
validators.forEach(validator -> {
    Object value = validator.valueSupplier().get();
    if (value != null) {
        int size = validator.sizeExtractor().apply(value);
        rejectValueIf(
            size > bulkSearchLimit,
            errors,
            validator.fieldName(),
            ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED
        );
    }
});

方案2:利用Spring Validation自定义注解(Spring项目专属)

如果是Spring环境,通过自定义校验注解和校验器,将校验逻辑从业务代码中剥离,实现声明式校验,代码更简洁易维护。

步骤1:定义自定义校验注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MaxAllowedSizeValidator.class)
public @interface MaxAllowedSize {
    String message() default ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED;
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

步骤2:实现校验器逻辑

public class MaxAllowedSizeValidator implements ConstraintValidator<MaxAllowedSize, Object> {
    @Autowired
    private ContactIndexConfigurationProperties config;

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if (value == null) {
            return true; // 空值不校验,对应原逻辑
        }
        int size = calculateSize(value);
        return size <= config.getBulkSearchLimit();
    }

    // 统一处理不同类型的大小计算
    private int calculateSize(Object value) {
        if (value instanceof Collection) {
            return ((Collection<?>) value).size();
        } else if (value instanceof String) {
            return ((String) value).length();
        } else if (value instanceof Number) {
            return ((Number) value).intValue();
        }
        throw new IllegalArgumentException("不支持的字段类型:" + value.getClass().getName());
    }
}

步骤3:在Body类字段上标注注解

public class Body {
    @MaxAllowedSize
    private List<String> cprNumber;

    @MaxAllowedSize
    private List<String> cvrNumber;

    @MaxAllowedSize
    private List<String> id;

    @MaxAllowedSize
    private Integer size;

    @MaxAllowedSize
    private String next;

    @MaxAllowedSize
    private Integer page;

    @MaxAllowedSize
    private List<String> sortFields;

    @MaxAllowedSize
    private List<String> fields;

    @MaxAllowedSize
    private Map<String, String> searchParameters;

    @MaxAllowedSize
    private List<String> limitSearchToFields;

    @MaxAllowedSize
    private Integer maxSize;

    @MaxAllowedSize
    private Integer defaultSize;

    // getter/setter 省略
}

步骤4:在Controller中启用校验

@PostMapping("/your-endpoint")
public ResponseEntity<?> yourMethod(@Valid @RequestBody Body body, Errors errors) {
    if (errors.hasErrors()) {
        // 处理错误逻辑
    }
    // 业务逻辑
    return ResponseEntity.ok().build();
}

方案3:提取重复逻辑到工具方法(快速简化)

如果不想做大规模重构,可以将重复的校验逻辑提取到工具方法,减少代码重复,同时降低认知复杂度。

步骤1:定义工具方法

private <T> void validateField(Supplier<T> valueSupplier, Function<T, Integer> sizeExtractor, String fieldName) {
    T value = valueSupplier.get();
    if (value != null) {
        int size = sizeExtractor.apply(value);
        rejectValueIf(
            size > contactIndexConfigurationProperties.getBulkSearchLimit(),
            errors,
            fieldName,
            ValidationConstants.ErrorCodes.NUMBER_OF_SEARCHES_NOT_ALLOWED
        );
    }
}

步骤2:调用工具方法

validateField(body::getCprNumber, Collection::size, ValidationConstants.Fields.CPR_NUMBER);
validateField(body::getCvrNumber, Collection::size, ValidationConstants.Fields.CVR_NUMBER);
validateField(body::getId, Collection::size, ValidationConstants.Fields.ID);
validateField(body::getSize, size -> size, ValidationConstants.Fields.SIZE);
validateField(body::getNext, String::length, ValidationConstants.Fields.NEXT);
validateField(body::getPage, page -> page, ValidationConstants.Fields.PAGE);
validateField(body::getSortFields, Collection::size, ValidationConstants.Fields.SORT_FIELDS);
validateField(body::getFields, Collection::size, ValidationConstants.Fields.FIELDS_CONSTANT);
validateField(body::getSearchParameters, Collection::size, ValidationConstants.Fields.SEARCH_PARAMETERS);
validateField(body::getLimitSearchToFields, Collection::size, ValidationConstants.Fields.LIMIT_SEARCH_TO_FIELDS);
validateField(body::getMaxSize, maxSize -> maxSize, ValidationConstants.Fields.MAX_SIZE);
validateField(body::getDefaultSize, defaultSize -> defaultSize, ValidationConstants.Fields.DEFAULT_SIZE);

内容的提问来源于stack exchange,提问作者FilipA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 06:17:07