[英]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.