降低函数的Cyclomatic复杂度

时间:2017-12-28 09:33:46

标签: java refactoring cyclomatic-complexity

我有一个函数,它使用JPA Criteria API从数据库中检索数据,具体取决于我传递给它的一些参数。 params使用对象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抱怨这种方法的循环复杂性&#34;发现&#34;是31大于30授权,我做了一些研究,我发现我必须重构我的功能,以减少Cyclomatic复杂性。

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

最终我需要查看很多字段,如果没有大量的检查,我无法看到如何做到这一点:/

我的情况如何解决这个问题?

3 个答案:

答案 0 :(得分:1)

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

如果你看一下你的方法,你有很多部分:

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

这些是使用Extract Method重构重构为单独方法的理想选择。将这些部分提取到新方法后,可以对每个方法重复该过程,直到您认为已经尽可能远。然后,您可以查看已创建的新方法,并识别那些操纵相同数据的方法。他们可能希望被移动到一个单独的类,例如,一个Query类。

switch语句看起来也是替换有条件多态性重构的好候选者。

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

答案 1 :(得分:0)

以下是提取方法的方法:

public List<MyObjectDTO> find(final GridParamsDTO gridParams) {
    final CriteriaBuilder builder = getCriteriaBuilder();

    final CriteriaQuery<MyObjectDTO> criteriaQuery = builder.createQuery(MyObjectDTO.class);
    final Root<MyObject> from = criteriaQuery.from(MyObject.class);
    final Join<MyObject, AnotherObject> join = from.join(MyObject_.anotherObject, JoinType.LEFT);

    final List<Order> orderList = buildOrderList(gridParams, builder, from, join);
    if (!orderList.isEmpty()) {
        criteriaQuery.orderBy(orderList);
    }

    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(buildPredicates(gridParams, builder, criteriaQuery, from, join)));
    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();
}

private Predicate[] buildPredicates(final GridParamsDTO gridParams, final CriteriaBuilder builder,
        final CriteriaQuery<MyObjectDTO> criteriaQuery, final Root<MyObject> from,
        Join<MyObject, AnotherObject> join) {
    final List<Predicate> predicates = new ArrayList<>();
    final GridFilterDTO[] filters = gridParams.getFilter();
    if (filters != null && filters.length > 0) {
        for (final GridFilterDTO filter : filters) {
            if ("fieldX".equals(filter.getField())) {
                addSubQuery(builder, predicates, criteriaQuery, from, filter);
            }
            if ("fieldA".equals(filter.getField()) || "fieldB".equals(filter.getField())) {
                addTextPredicates(builder, predicates, from, filter);
            }
            if ("dateField1".equals(filter.getField()) || "dateField2".equals(filter.getField())) {
                addDatePredicates(builder, predicates, from, filter);
            }
            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());
                }
            }
        }
    }
    predicates.add(builder.between(builder.literal(LocalDate.now()), from.get(MyObject_.dateDebut),
            from.get(MyObject_.dateFin)));
    return predicates.toArray(new Predicate[predicates.size()]);
}

private void addSubQuery(final CriteriaBuilder builder, final List<Predicate> predicates,
        final CriteriaQuery<MyObjectDTO> criteriaQuery, final Root<MyObject> from, final GridFilterDTO filter) {
    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));
}

private void addTextPredicates(final CriteriaBuilder builder, final List<Predicate> predicates,
        final Root<MyObject> from, final GridFilterDTO filter) {
    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;
    }
}

private void addDatePredicates(final CriteriaBuilder builder, final List<Predicate> predicates,
        final Root<MyObject> from, final GridFilterDTO filter) {
    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;
    }
}

private List<Order> buildOrderList(final GridParamsDTO gridParams, final CriteriaBuilder builder,
        final Root<MyObject> from, Join<MyObject, AnotherObject> join) {
    if (gridParams.hasSort()) {
        return Collections.emptyList();
    }
    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)));
            }
        }
    }
    return orderList;
}

现在由你来决定这是否更好。 您甚至可以提取更多方法和更多局部变量,以避免在此代码中重复过多。

答案 2 :(得分:-1)

你可以尝试这样的事情:

&#xA;&#xA;
  public List&lt; MyObjectDTO&gt; find(final GridParamsDTO gridParams){&#xA;&#xA; CriteriaBuilder builder = getCriteriaBuilder();&#xA;列表与LT;谓词&GT; predicates = new ArrayList&lt;&gt;();&#xA;&#xA; CriteriaQuery中&LT; MyObjectDTO&GT; criteriaQuery = builder.createQuery(MyObjectDTO.class);&#xA;&#xA;根和LT;为MyObject&GT; from = criteriaQuery.from(MyObject.class);&#xA;&#xA;加入&lt; MyObject,AnotherObject&gt; join = from.join(MyObject_.anotherObject,JoinType.LEFT);&#xA;&#xA; predicates = subMethod1(gridParams,builder,predicates,criteriaQuery,from,join);&#xA;&#xA; criteriaQuery = subMethod2(gridParams,builder,criteriaQuery,from,join);&#xA;&#xA; predicates.add(builder.between(builder.literal(LocalDate.now()),from.get(MyObject_.dateDebut),&#xA; from.get(MyObject_.dateFin)));&#xA;&#xA ; criteriaQuery.multiselect(from.get(MyObject_.objectX),from.get(MyObject_.dateDebut),from.get(MyObject_.dateFin),&#xA; from.get(MyObject_.lib),from.get(MyObject_。) coleur),join,&#xA; from.get(MyObject_.refExterne));&#xA; criteriaQuery.where(builder.and(predicates.toArray(new Predicate [predicates.size()])));&#xA; criteriaQuery.distinct(真);&#XA;&#XA;最终的TypedQuery&lt; MyObjectDTO&gt; typedQuery = getEntityManager()。createQuery(criteriaQuery);&#xA;&#xA; if(gridParams.hasStart()){&#xA; typedQuery.setFirstResult(gridParams.getStartRow());&#XA; }&#XA; if(gridParams.hasEnd()){&#xA; typedQuery.setMaxResults(gridParams.getEndRow() -  Math.max(0,gridParams.getStartRow()));&#xA; }&#XA;&#XA; return typedQuery.getResultList();&#xA;}&#xA;&#xA; private CriteriaQuery&lt; MyObjectDTO&gt; subMethod2(GridParamsDTO gridParams,CriteriaBuilder构建器,&#xA; CriteriaQuery&lt; MyObjectDTO&gt; criteriaQuery,Root&lt; MyObject&gt; from,&#xA; Join&lt; MyObject,AnotherObject&gt; join){&#xA; if(gridParams.hasSort()){&#xA;列表与LT;排序&gt; orderList = new ArrayList&lt;&gt;();&#xA; for(IGridSort gridSort:gridParams.getSort()){&#xA; if(ArrayUtils.contains(new String [] {“fieldA”,“dateField1”,“dateField2”,“fieldB”},gridSort.getField())){&#xA; if(gridSort.getSort()。equalsIgnoreCase(GridSortDTO.ASC)){&#xA; orderList.add(builder.asc(from.get(gridSort.getField())));&#XA; } else if(gridSort.getSort()。equalsIgnoreCase(GridSortDTO.DESC)){&#xA; orderList.add(builder.desc(from.get(gridSort.getField())));&#XA; }&#XA; }&#XA; if(gridSort.getField()。equalsIgnoreCase(“anotherField”)){&#xA; if(gridSort.getSort()。equalsIgnoreCase(GridSortDTO.ASC)){&#xA; orderList.add(builder.asc(join.get(AnotherObject_.lib)));&#XA; } else if(gridSort.getSort()。equalsIgnoreCase(GridSortDTO.DESC)){&#xA; orderList.add(builder.desc(join.get(AnotherObject_.lib)));&#XA; }&#XA; }&#XA; }&#XA; if(!orderList.isEmpty()){&#xA; criteriaQuery.orderBy(orderList);&#XA; }&#XA; }&#XA; return criteriaQuery;&#xA;}&#xA;&#xA; private List&lt; Predicate&gt; subMethod1(GridParamsDTO gridParams,CriteriaBuilder构建器,&#xA; List&lt; Predicate&gt;谓词,CriteriaQuery&lt; MyObjectDTO&gt; criteriaQuery,Root&lt; MyObject&gt;来自&#xA; Join&lt; MyObject,AnotherObject&gt; join){&#xA; if((gridParams.getFilter()!= null)&amp;&amp;(gridParams.getFilter()。length&gt; 0)){&#xA;&#xA; for(final GridFilterDTO filter:gridParams.getFilter()){&#xA; if(“fieldX”.equals(filter.getField())){&#xA;最终列表&lt;谓词&gt; sqPredicates = new ArrayList&lt;&gt;();&#xA;子查询&LT;长&GT; sq = criteriaQuery.subquery(Long.class);&#xA;根和LT; _对象&GT; sqFrom = sq.from(ObjectX.class);&#xA; sqPredicates&#XA; .add(builder.equal(sqFrom.get(ObjectX_.organismeIDE),filter.getFilterSingleValue()));&#xA; sq.select(sqFrom.get(ObjectX_.referentielID)获得(MyObject_.objectX));&#XA; sq.where(builder.and(sqPredicates.toArray(new Predicate [sqPredicates.size()])));&#xA; predicates.add(builder.in(from.get(MyObject_.objectX))值(SQ)。);&#XA; } else if(“fieldA”.equals(filter.getField())||“fieldB”.equals(filter.getField())){&#xA; final GridFilterOperatorEnum gridFilterEnumValues = GridFilterOperatorEnum.values()[filter.getOperator()。ordinal()];&#xA; switch(gridFilterEnumValues){&#xA;案例TEXT_CONTAINS:&#xA; predicates.add(builder.like(builder.upper(from.get(filter.getField())),“%”+ filter.getFilterSingleValue()。toUpperCase()+“%”));&#xA;打破;&#XA; case TEXT_START_WITH:&#xA; predicates.add(builder.like(builder.upper(from.get(filter.getField())),filter.getFilterSingleValue()。toUpperCase()+“%”));&#xA;打破;&#XA;案例TEXT_DIFFERENT:&#xA; predicates.add(builder.notEqual(from.get(filter.getField()),filter.getFilterSingleValue()));&#xA;打破;&#XA;案例TEXT_END_WITH:&#xA; predicates.add(builder.like(builder.upper(from.get(filter.getField())),“%”+ filter.getFilterSingleValue()。toUpperCase()));&#xA;打破;&#XA;案例TEXT_EQUALS:&#xA; predicates.add(builder.equal(builder.upper(from.get(filter.getField())),filter.getFilterSingleValue()。toUpperCase()));&#xA;打破;&#XA;默认:&#XA;打破;&#XA; }&#XA; } else if(“dateField1”.equals(filter.getField())||“dateField2”.equals(filter.getField())){&#xA; final GridFilterOperatorEnum gridFilterEnumValues = GridFilterOperatorEnum.values()[filter.getOperator()。ordinal()];&#xA; switch(gridFilterEnumValues){&#xA;案例DATE_EQUALS:&#xA; predicates.add(builder.equal(from。&lt; LocalDate&gt; get(filter.getField()),LocalDate.parse(filter.getFilterSingleValue())));&#xA;打破;&#XA;案例DATE_BEFORE:&#xA; predicates.add(builder.lessThan(from。&lt; LocalDate&gt; get(filter.getField()),LocalDate.parse(filter.getFilterSingleValue())));&#xA;打破;&#XA;案例DATE_AFTER:&#xA; predicates.add(builder.greaterThan(from。&lt; LocalDate&gt; get(filter.getField()),LocalDate.parse(filter.getFilterSingleValue())));&#xA;打破;&#XA;案例DATE_BETWEEN:&#xA; predicates.add(&#xA; builder.between(from。&lt; LocalDate&gt; get(filter.getField()),LocalDate.parse(filter.getFilterSetValues()[0]),&#xA; LocalDate.parse(filter .getFilterSetValues()[1])));&#XA;打破;&#XA;默认:&#XA;打破;&#XA; }&#XA; } else if(“anotherField”.equals(filter.getField())){&#xA; if(filter.getFilterSetValues()。length&gt; 0){&#xA;谓词&#XA; .add(builder.isTrue(join.get(AnotherObject_.lib).in((Object [])filter.getFilterSetValues())));&#xA; } else {&#xA; predicates.add(join.get(AnotherObject_.lib).isNull());&#XA; }&#XA;&#XA; }&#XA; }&#XA;&#XA; }&#XA;返回谓词;&#xA;}&#xA;  
&#xA;