【问题标题】:How can I refactor this code having multiple if-else?如何重构具有多个 if-else 的代码?
【发布时间】:2020-08-20 10:24:54
【问题描述】:

我有一堂有很多“if else”的课。我不知道如何重构它。我想到了工厂模式,但我不知道如何实现它。有人帮我吗?

    @Override
    public Predicate toPredicate(@NotNull Root<E> root, @NotNull CriteriaQuery<?> cq, @NotNull CriteriaBuilder cb) {

        List<Predicate> predicates = new ArrayList<>();

        for (SearchCriteria criteria : criteriaList) {
            if (criteria.getValue() == null) {
                predicates.add(cb.isTrue(cb.literal(true)));
            } else if (criteria.getOperation().equals(GREATER_THAN)) {
                predicates.add(cb.greaterThan(root.get(criteria.getKey()).as(criteria.getKey().getClass()), criteria.getValue().toString()));
            } else if (criteria.getOperation().equals(LESS_THAN)) {
                predicates.add(cb.lessThan(root.get(criteria.getKey()).as(criteria.getKey().getClass()), criteria.getValue().toString()));
            } else if (criteria.getOperation().equals(GREATER_THAN_EQUAL)) {
                predicates.add(cb.greaterThanOrEqualTo(root.get(criteria.getKey()).as(criteria.getKey().getClass()), criteria.getValue().toString()));
            } else if (criteria.getOperation().equals(LESS_THAN_EQUAL)) {
                predicates.add(cb.lessThanOrEqualTo(root.get(criteria.getKey()).as(criteria.getKey().getClass()), criteria.getValue().toString()));
            } else if (criteria.getOperation().equals(NOT_EQUAL)) {
                predicates.add(cb.notEqual(root.get(criteria.getKey()).as(criteria.getKey().getClass()), criteria.getValue()));
            } else if (criteria.getOperation().equals(EQUAL)) {
                predicates.add(cb.equal(root.get(criteria.getKey()).as(criteria.getKey().getClass()), criteria.getValue()));
            } else if (criteria.getOperation().equals(MATCH)) {
                predicates.add(cb.like(cb.lower(root.get(criteria.getKey())), "%" + criteria.getValue().toString().toLowerCase() + "%"));
            } else if (criteria.getOperation().equals(MATCH_END)) {
                predicates.add(cb.like(cb.lower(root.get(criteria.getKey())), criteria.getValue().toString().toLowerCase() + "%"));
            } else if (criteria.getOperation().equals(MATCH_START)) {
                predicates.add(cb.like(cb.lower(root.get(criteria.getKey())), "%" + criteria.getValue().toString().toLowerCase()));
            } else if (criteria.getOperation().equals(IN)) {
                predicates.add(cb.in(root.get(criteria.getKey())).value(criteria.getValue()));
            } else if (criteria.getOperation().equals(NOT_IN)) {
                predicates.add(cb.not(root.get(criteria.getKey())).in(criteria.getValue()));
            }
        }
        return cb.and(predicates.toArray(Predicate[]::new));
    }

【问题讨论】:

  • 看起来你可以使用 switch 语句。
  • 我想使用一个模式。
  • 你可以在操作对象中添加一个构造谓词的方法,这样你最终会做一些类似 predicates.add(criteria.getOperation().createPredicate(root, criteria)) 的事情,只剩下特殊情况会成为你的第一个测试 null 的 if 语句。

标签: java spring-boot design-patterns spring-data-jpa


【解决方案1】:

SearchCriteria.java接口,类似这样:

public interface SearchCriteria {

    String getOperation();
}

而不是编写将实现此接口的 Criteria 类,如下所示:

public class LessThanCriteria implements SearchCriteria {

    public static final String LESS = "some value";

    @Override
    public String getOperation() {
        return LESS;
    }
}

public class GreatherThanCriteria implements SearchCriteria {

    public static final String GREATER_THAN = "some value";

    @Override
    public String getOperation() {
        return GREATER_THAN;
    }
}

等等。

比编写PredicateFactory.java 类来封装基于 SearchCriteria 创建谓词对象的逻辑。

public class PredicateFactory {

    public static final String LESS = "some value";
    public static final String LESS_THAN_EQUAL = "some value";
    public static final String NOT_EQUAL = "some value";
    public static final String EQUAL = "some value";
    public static final String MATCH = "some value";
    public static final String MATCH_END = "some value";
    public static final String MATCH_START = "some value";
    public static final String IN = "some value";
    public static final String NOT_IN = "some value";

    public static Predicate getPredicate(SearchCriteria criteria){
        if(criteria == null){
            return null;
        }
        if(criteria.getOperation().equalsIgnoreCase(LESS)){
            return new Predicate(); // here you create predicate object that 
                                          // you want, 
                                        //based on criteria

        } else if(criteria.getOperation().equalsIgnoreCase(LESS_THAN_EQUAL)){
            return new Predicate();

        } else if(criteria.getOperation().equalsIgnoreCase(NOT_EQUAL)){
            return new Predicate();
        }
        else if(criteria.getOperation().equalsIgnoreCase(EQUAL)){
             return new Predicate();
        }
        else if(criteria.getOperation().equalsIgnoreCase(MATCH)){
            return new Predicate();
        }
        else if(criteria.getOperation().equalsIgnoreCase(MATCH_END)){
            return new Predicate();
        }
        else if(criteria.getOperation().equalsIgnoreCase(MATCH_START)){
            return new Predicate();
        }
        else if(criteria.getOperation().equalsIgnoreCase(IN)){
            return new Predicate();
        }
        else if(criteria.getOperation().equalsIgnoreCase(NOT_IN)){
            return new Predicate();
        }
        return null;
    }
    
}

最后,在 foo 循环中使用这个 Factory 类:

for (SearchCriteria criteria : criteriaList) {
      Predicate p = PredicateFactory.getPredicate(criteria);
      predicates.add(p); 

}

【讨论】:

  • @Henry 您将对象创建的逻辑封装在一个地方,这是一个工厂类。如果您需要添加新条件,您可以创建将实现 SearchCriteria 接口的新类,然后将该条件添加到工厂类中的一个位置。
【解决方案2】:

Factory Pattern 是一种创造模式。因此,它用于构造对象,并且在各种用途之间需要存在一些通用接口。这暂时不适用于您的案例。

假设您可以编辑 CriteriaBuilder,您可以执行以下操作:

  1. (可选)将criteria.getOperation()criteria.getKey()criteria.value() 替换为局部变量opkeyvalue,以使代码更具可读性。
  2. 更新 CriteriaBuilder 以公开像 build(root, key, value, op) 这样的单个方法,并将如何构建每个条件的代码移到此方法中。
  3. 在您的toPredicate() 中,您可以简单地predicates.add(cb.build(root, key, value, op))

您可能会争辩说,这种方法只会将巨大的 if-else 块移动到 cb.build() 方法中;然而,这离更简洁的代码又近了一步。 cb.build() 本质上是您要查找的工厂方法,调用者 (toPredicate()) 不必了解构建条件的逻辑。

您可以继续将 if-else 逻辑改进为 cb.build(),因为您可以直接访问 CriteriaBuilder 中的私有字段。如果不查看CriteriaBuilder 的代码,很难判断下一步是什么。

【讨论】:

    【解决方案3】:

    你可以检查一下,找到 Eugen Paraschiv 的一个很好的例子

    @Override
    public Predicate toPredicate(final Root<T> root, final CriteriaQuery<?> query, final CriteriaBuilder builder) {
        final List<Object> args = castArguments(root);
        final Object argument = args.get(0);
        switch (RsqlSearchOperation.getSimpleOperator(operator)) {
    
        case EQUAL: {
            if (argument instanceof String) {
                return builder.like(root.get(property), argument.toString().replace('*', '%'));
            } else if (argument == null) {
                return builder.isNull(root.get(property));
            } else {
                return builder.equal(root.get(property), argument);
            }
        }
        case NOT_EQUAL: {
            if (argument instanceof String) {
                return builder.notLike(root.<String> get(property), argument.toString().replace('*', '%'));
            } else if (argument == null) {
                return builder.isNotNull(root.get(property));
            } else {
                return builder.notEqual(root.get(property), argument);
            }
        }
        case GREATER_THAN: {
            return builder.greaterThan(root.<String> get(property), argument.toString());
        }
        case GREATER_THAN_OR_EQUAL: {
            return builder.greaterThanOrEqualTo(root.<String> get(property), argument.toString());
        }
        case LESS_THAN: {
            return builder.lessThan(root.<String> get(property), argument.toString());
        }
        case LESS_THAN_OR_EQUAL: {
            return builder.lessThanOrEqualTo(root.<String> get(property), argument.toString());
        }
        case IN:
            return root.get(property).in(args);
        case NOT_IN:
            return builder.not(root.get(property).in(args));
        }
    
        return null;
    }
    
    // === private
    
    private List<Object> castArguments(final Root<T> root) {
        
        final Class<? extends Object> type = root.get(property).getJavaType();
        
        final List<Object> args = arguments.stream().map(arg -> {
            if (type.equals(Integer.class)) {
               return Integer.parseInt(arg);
            } else if (type.equals(Long.class)) {
               return Long.parseLong(arg);
            } else {
                return arg;
            }            
        }).collect(Collectors.toList());
    
        return args;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多