java - 降低函数的圈复杂度

标签 java refactoring cyclomatic-complexity

我有一个函数,它使用 JPA Criteria API 根据我传递给它的一些参数从数据库检索数据。 使用对象 gridParams 传递参数,如下所示:

public List<MyObjectDTO> find(final GridParamsDTO gridParams) {

        final CriteriaBuilder builder = getCriteriaBuilder();
        final List<Predicate> predicates = new ArrayList<>();

        final CriteriaQuery<MyObjectDTO> criteriaQuery = builder.createQuery(MyObjectDTO.class);

        final Root<MyObject> from = criteriaQuery.from(MyObject.class);

        Join<MyObject, AnotherObject> join = from.join(MyObject_.anotherObject, JoinType.LEFT);

        if ((gridParams.getFilter() != null) && (gridParams.getFilter().length > 0)) {

            for (final GridFilterDTO filter : gridParams.getFilter()) {
                if ("fieldX".equals(filter.getField())) {
                    final List<Predicate> sqPredicates = new ArrayList<>();
                    Subquery<Long> sq = criteriaQuery.subquery(Long.class);
                    Root<ObjectX> sqFrom = sq.from(ObjectX.class);
                    sqPredicates
                            .add(builder.equal(sqFrom.get(ObjectX_.organismeIDE), filter.getFilterSingleValue()));
                    sq.select(sqFrom.get(ObjectX_.referentielID).get(MyObject_.objectX));
                    sq.where(builder.and(sqPredicates.toArray(new Predicate[sqPredicates.size()])));
                    predicates.add(builder.in(from.get(MyObject_.objectX)).value(sq));
                }
                if ("fieldA".equals(filter.getField()) || "fieldB".equals(filter.getField())) {
                    final GridFilterOperatorEnum gridFilterEnumValues = GridFilterOperatorEnum.values()[filter.getOperator().ordinal()];
                    switch (gridFilterEnumValues) {
                    case TEXT_CONTAINS:
                        predicates.add(builder.like(builder.upper(from.get(filter.getField())), "%" + filter.getFilterSingleValue().toUpperCase() + "%"));
                        break;
                    case TEXT_START_WITH:
                        predicates.add(builder.like(builder.upper(from.get(filter.getField())), filter.getFilterSingleValue().toUpperCase() + "%"));
                        break;
                    case TEXT_DIFFERENT:
                        predicates.add(builder.notEqual(from.get(filter.getField()), filter.getFilterSingleValue()));
                        break;
                    case TEXT_END_WITH:
                        predicates.add(builder.like(builder.upper(from.get(filter.getField())), "%" + filter.getFilterSingleValue().toUpperCase()));
                        break;
                    case TEXT_EQUALS:
                        predicates.add(builder.equal(builder.upper(from.get(filter.getField())), filter.getFilterSingleValue().toUpperCase()));
                        break;
                    default:
                        break;
                    }
                }
                if ("dateField1".equals(filter.getField()) || "dateField2".equals(filter.getField())) {
                    final GridFilterOperatorEnum gridFilterEnumValues = GridFilterOperatorEnum.values()[filter.getOperator().ordinal()];
                    switch (gridFilterEnumValues) {
                    case DATE_EQUALS:
                        predicates.add(builder.equal(from.<LocalDate> get(filter.getField()), LocalDate.parse(filter.getFilterSingleValue())));
                        break;
                    case DATE_BEFORE:
                        predicates.add(builder.lessThan(from.<LocalDate> get(filter.getField()), LocalDate.parse(filter.getFilterSingleValue())));
                        break;
                    case DATE_AFTER:
                        predicates.add(builder.greaterThan(from.<LocalDate> get(filter.getField()), LocalDate.parse(filter.getFilterSingleValue())));
                        break;
                    case DATE_BETWEEN:
                        predicates.add(
                                builder.between(from.<LocalDate> get(filter.getField()), LocalDate.parse(filter.getFilterSetValues()[0]),
                                        LocalDate.parse(filter.getFilterSetValues()[1])));
                        break;
                    default:
                        break;
                    }
                }
                if ("anotherField".equals(filter.getField())) {
                    if (filter.getFilterSetValues().length > 0) {
                        predicates
                                .add(builder.isTrue(join.get(AnotherObject_.lib).in((Object[]) filter.getFilterSetValues())));
                    } else {
                        predicates.add(join.get(AnotherObject_.lib).isNull());
                    }

                }
            }

        }

        if (gridParams.hasSort()) {
            List<Order> orderList = new ArrayList<>();
            for (IGridSort gridSort : gridParams.getSort()) {
                if (ArrayUtils.contains(new String[] { "fieldA", "dateField1", "dateField2", "fieldB" }, gridSort.getField())) {
                    if (gridSort.getSort().equalsIgnoreCase(GridSortDTO.ASC)) {
                        orderList.add(builder.asc(from.get(gridSort.getField())));
                    } else if (gridSort.getSort().equalsIgnoreCase(GridSortDTO.DESC)) {
                        orderList.add(builder.desc(from.get(gridSort.getField())));
                    }
                }
                if (gridSort.getField().equalsIgnoreCase("anotherField")) {
                    if (gridSort.getSort().equalsIgnoreCase(GridSortDTO.ASC)) {
                        orderList.add(builder.asc(join.get(AnotherObject_.lib)));
                    } else if (gridSort.getSort().equalsIgnoreCase(GridSortDTO.DESC)) {
                        orderList.add(builder.desc(join.get(AnotherObject_.lib)));
                    }
                }
            }
            if (!orderList.isEmpty()) {
                criteriaQuery.orderBy(orderList);
            }
        }

        predicates.add(builder.between(builder.literal(LocalDate.now()), from.get(MyObject_.dateDebut),
                from.get(MyObject_.dateFin)));

        criteriaQuery.multiselect(from.get(MyObject_.objectX), from.get(MyObject_.dateDebut), from.get(MyObject_.dateFin),
                from.get(MyObject_.lib), from.get(MyObject_.coleur), join,
                from.get(MyObject_.refExterne));
        criteriaQuery.where(builder.and(predicates.toArray(new Predicate[predicates.size()])));
        criteriaQuery.distinct(true);

        final TypedQuery<MyObjectDTO> typedQuery = getEntityManager().createQuery(criteriaQuery);

        if (gridParams.hasStart()) {
            typedQuery.setFirstResult(gridParams.getStartRow());
        }
        if (gridParams.hasEnd()) {
            typedQuery.setMaxResults(gridParams.getEndRow() - Math.max(0, gridParams.getStartRow()));
        }

        return typedQuery.getResultList();
    }
}

问题是 SonarQube 提示这个方法“find”的循环复杂度是 31,大于授权的 30,我做了一些研究,发现我必须重构我的函数以降低循环复杂度。

在此函数中,我必须测试 gridParams 是否定义了 4 个字段:(开始、结束、排序和过滤器),排序和过滤器是对象数组,因此我必须迭代它们。

最终我需要检查很多字段,但我不知道如何在没有大量检查的情况下完成此操作:/

我该如何解决这个问题?

最佳答案

您可能想首先查看 Refactoring Catalog 中的一些重构。 .

如果你看看你的方法,你会发现有很多部分:

  • 编译谓词列表的部分
  • 编译订单列表的部分
  • 创建查询的部分
  • 运行查询的部分

这些是使用提取方法重构重构为单独方法的良好候选者。一旦这些部分被提取到新的方法中,就可以在每种方法上重复该过程,直到您认为您已经完成了尽可能多的操作。然后,您可以查看您创建的新方法并识别那些操作相同数据的方法。他们可能希望转移到单独的类,例如查询类。

switch 语句看起来也很适合用多态性重构替换条件。

如果有疑问,请从小处开始,一次进行一个简单的重构。

关于java - 降低函数的圈复杂度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48004930/

相关文章:

java - 私有(private)静态方法中的最终字符串在调用时是否会实例化一个新对象?

java - 将项目添加到 ListView 适配器中的特定列

java - 使用脚本重构 Java 代码

C# Switch 语句重构

java - 在 TDD 中,在重构期间将主类拆分为子类

python - 降低面向对象的Python代码的圈复杂度

java - 如何清除文本框中的文字?

java - payara 上的 NoClassDefFoundError

php - switch/cases 和 in_array 之间的循环复杂度差异

c# - 为什么使用 lambda 进行列表初始化会导致高圈复杂度?