Java 多种组合的设计模式

Java 多种组合的设计模式,java,design-patterns,repository,Java,Design Patterns,Repository,如果我必须根据不同参数的存在与否进行不同的数据库查询,那么哪种设计模式才是正确的,以避免使用不同的组合进行过多的If-else查询? 假设我有参数a、b、c(数量在未来可能会增加),我使用的是存储库,所以我必须打一个类似这样的电话 public Foo getFoo(String a, String b, String c){ Foo foo; if(a!=null && !a.isEmpty() && b!=null && !b.is

如果我必须根据不同参数的存在与否进行不同的数据库查询,那么哪种设计模式才是正确的,以避免使用不同的组合进行过多的If-else查询? 假设我有参数a、b、c(数量在未来可能会增加),我使用的是存储库,所以我必须打一个类似这样的电话

public Foo getFoo(String a, String b, String c){
   Foo foo;
   if(a!=null && !a.isEmpty() && b!=null && !b.isEmpty() && c!=null && !c.isEmpty())
      foo = repository.findByAAndBAndC(a,b,c);
   if((a==null || a.isEmpty()) && b!=null && !b.isEmpty() && c!=null && !c.isEmpty())
      foo = repository.findByBAndC(b,c);
   if((a!=null && !a.isEmpty()) && (b==null || b.isEmpty()) && c!=null && !c.isEmpty())
      foo = repository.findByAAndC(a,c);
   if((a==null || a.isEmpty()) && (b==null || b.isEmpty()) && !b.isEmpty() && c!=null && !c.isEmpty())
      foo = repository.findByC(c);
   if((a==null || a.isEmpty()) && (b==null || b.isEmpty()) && !b.isEmpty() && (b==null || b.isEmpty()))
      foo = repository.findOne();
   etc.
   .
   .
   .
   return foo;
}

怎样才能更好地组织呢?

这不是一个完整的答案。我将提出几个建议来解决目前的问题

处理空值

为了避免检查值是否为
null
,我建议您为字符串查询参数使用一个容器类,并使用某种方法,例如
getValue()
,返回参数的值,例如,
parameter='value'
(如果值存在)或一些默认字符串值,例如。,
参数,如“%”
,如果它是
null
。这种方法遵循所谓的空设计模式

查询的动态构造

执行此操作后,您传递的参数值将不再重要,您可以迭代构造条件,例如:

for parameter in parameters:
    condition = "AND" + parameter.getValue()
也许您可以将其与接受任意长度条件的通用查询方法相结合,例如:

repository.findBy(condition)

我不是100%确定,因为我是从我的头脑中输入这个答案的,但我认为这种方法是有效的,应该能够解决你的帖子中提到的问题。让我知道您的想法。

首先,我会向您推荐以下设计模式:

是一种特定的软件设计模式,通过该模式可以创建业务规则 通过使用布尔值将业务规则链接在一起来重新组合 逻辑。该模式经常在域驱动的环境中使用 设计

但是您的实际代码并不完全适合这种情况,因为您没有根据具体情况调用存储库的相同方法。
所以我认为你有两种方法:

1) 重构存储库,以提供一个接受规范参数并能够处理不同情况的通用方法。
如果使用Spring,您可以查看
JpaSpecificationExecutor
接口,该接口提供了以下方法:

List<T> findAll(Specification<T> spec)
其中
findfooststrategy
定义为:

public interface FindFooStrategy{
  boolean isApplicable(String a, String b, String c);
  Foo getFoo(String a, String b, String c);
}
每个子类都定义了自己的规则。例如:

public class FindFooByAAndBAndCStrategy implements FindFooStrategy{
  public boolean isApplicable(String a, String b, String c){
      return StringUtils.isNotEmpty(a) && StringUtils.isNotEmpty(b) &&
             StringUtils.isNotEmpty(c);
  }
  public Foo getFoo(String a, String b, String c){
      return repository.findByAAndBAndC(a,b,c);
  } 
}

可以使用枚举定义位图常量,方法为valueOf:

public enum Combinations{
    A_AND_B_AND_C (0b111),
    B_AND_C       (0b110),
    A_AND_C       (0b101),
    C             (0b100),
    A_AND_B       (0b011),
    B             (0b010),
    A             (0b001),
    NONE          (0b000),
    ;

    private final int bitmap;

    Combinations(int bitmap){
        this.bitmap = bitmap;
    }

    public static Combinations valueOf(String... args){
        final StringBuilder builder = new StringBuilder();
        for(int i = args.length - 1; i >= 0; i--){
            final String arg = args[i];
            builder.append(arg != null && !arg.isEmpty() ? '1' : '0');
        }

        final int bitmap = Integer.parseInt(builder.toString(), 2);

        final Combinations[] values = values();
        for(int i = values.length -1; i >= 0; i--){
            if(values[i].bitmap == bitmap){
                return values[i];
            }
        }

        throw new NoSuchElementException();
    }
}
以及另一个具有switch case语句的类:

public class SomeClass {

    public Foo getFoo(String a, String b, String c){
         switch(Combinations.valueOf(a, b, c)){
             case A_AND_B_AND_C:
                 return repository.findByAAndBAndC(a, b, c);

             case B_AND_C:
                  return repository.findByBAndC(b, c);

             /* all other cases */

             case NONE:
                  return repository.findOne();

             default:
                  // type unknown
                  throw new UnsupportedOperationException();
         }
    }
}
首先,这可能需要做很多工作。但当你做到了,你会很高兴的。通过使用位图,可以有很多组合。
valueOf
方法负责找出实际应采用的组合。但是之后应该发生的事情不能一般地做。因此,当添加另一个参数
d
时,您将得到更多必须添加到
enum
的组合


总而言之,这种解决方案对于少量的参数来说是过分的。仍然很容易理解,因为逻辑被分成许多小部分。不过,最后还是没有绕过big switch语句。

一个好的开始是使用Apache StringUtils.isEmpty。所需的模式是动态查询构建。您最好重新考虑存储库中大量的
findByX
方法,而不是使用规范或标准模式,这通常是ORM像Hibernate或SpringData所提供的。IMO,细粒度的repo设计才是真正的问题。可能是最面向对象的方式
public class SomeClass {

    public Foo getFoo(String a, String b, String c){
         switch(Combinations.valueOf(a, b, c)){
             case A_AND_B_AND_C:
                 return repository.findByAAndBAndC(a, b, c);

             case B_AND_C:
                  return repository.findByBAndC(b, c);

             /* all other cases */

             case NONE:
                  return repository.findOne();

             default:
                  // type unknown
                  throw new UnsupportedOperationException();
         }
    }
}