繁体   English   中英

多种组合的设计模式

[英]Design pattern for multiple combinations

如果我必须根据是否存在不同的参数进行不同的数据库查询,那将是避免使用不同组合使用过多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;
}

如何更好地组织结构?

首先,我将为您提出以下规范设计模式:

是一种特殊的软件设计模式,通过使用布尔逻辑将业务规则链接在一起,可以重新组合业务规则。 该模式经常在域驱动设计的上下文中使用。

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

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

List<T> findAll(Specification<T> spec)

即使您不使用Spring,我也认为这些示例可以为您提供帮助。

2)如果您无法重构存储库,则应该寻找另一种方式,并提供有关可以将存储库方法/参数传递给的抽象级别。

实际上,您会根据输入参数调用具有不同参数的不同方法,但是无论如何您都将相同类型的对象返回给该方法的客户端: Foo 因此,为了避免条件语句,多态性是遵循的方法。
最终要处理的每种情况都是不同的策略。 因此,您可以拥有一个策略界面,并且可以确定用于将Foo返回给客户端的策略。

此外,如注释中所建议:多次重复a!=null && !a.isEmpty()并不是很好的气味。 这会造成很多重复,并使代码的可读性降低。 最好通过使用诸如Apache common或自定义方法之类的库来应用此处理。

public class FooService {

    private List<FindFooStrategy> strategies = new ArrayList<>();

    public  FooService(){
      strategies.add(new FindFooByAAndBAndCStrategy());
      strategies.add(new FindFooByBAndCStrategy());
      strategies.add(new FindFooByAAndCStrategy());
      strategies.add(new FindFooByCStrategy());
    }

    public Foo getFoo(String a, String b, String c){

       for (FindFooStrategy strategy : strategies){
            if (strategy.isApplicable(a, b, c)) {
               return strategy.getFoo(a, b, c);
            }
       }
     }
}

其中FindFooStrategy定义为:

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);
  } 
}

这不是一个完整的答案。 我将提供一些建议来解决当前的问题。

处理空值

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

动态构造查询

完成此操作后,您传递的参数具有什么值将不再重要,您可以迭代地构造条件,例如:

for parameter in parameters:
    condition = "AND" + parameter.getValue()

也许您可以将其与接受任意长度条件的通用查询方法相结合,例如:

repository.findBy(condition)

我不是百分百肯定的,因为我是从脑海中键入此答案的,但是我认为这种方法有效并且应该能够解决您帖子中提到的问题。 让我知道你的想法。

您可以使用通过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

总而言之,对于少量参数而言,这种解决方案是过大的。 仍然很容易理解,因为逻辑被分为许多小部分。 不过,您仍然还是无法绕开最后的大切换语句。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM