如果我必须根据是否存在不同的参数来进行不同的数据库查询,这将是正确的设计模式,以避免使用不同组合的过多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;
}
如何更好地构建?
答案 0 :(得分:5)
一开始,我会建议Specification设计模式:
是一种特定的软件设计模式,业务规则可以是 通过使用布尔值将业务规则链接在一起来重新组合 逻辑。该模式经常在域驱动的上下文中使用 设计。
但是您的实际代码并不完全适合,因为您根据情况没有调用存储库的相同方法。
所以我认为你有两种方式:
1)重构您的存储库以提供一个接受规范参数并能够处理不同情况的通用方法。
如果使用Spring,则可以查看提供方法的JpaSpecificationExecutor
接口,如:
List<T> findAll(Specification<T> spec)
即使您不使用Spring,我认为these examples可以帮助您。
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);
}
}
答案 1 :(得分:0)
这不是一个完整的答案。我将提出一些解决手头问题的建议。
处理空值
为了避免检查值是否为null
,我建议您使用某个方法为String查询参数使用容器类,比如getValue()
返回参数的值,例如parameter='value'
如果值存在或某个默认字符串值,例如parameter like '%'
,如果它是null
。这种方法遵循所谓的 Null设计模式。
动态构建查询
执行此操作后,您传递的参数的值将不再重要,您可以迭代地构建条件,例如:
for parameter in parameters:
condition = "AND" + parameter.getValue()
也许你可以将它与一个接受任意长度条件的查询的通用方法结合起来,例如:
repository.findBy(condition)
我不是百分百肯定,因为我从头脑中输入了这个答案,但我认为这种方法有效,应该能够解决你帖子中提到的问题。让我知道你的想法。
答案 2 :(得分:0)
您可以使用带有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
的组合。
总而言之,这个解决方案对于少量参数来说太过分了。仍然很容易理解,因为逻辑被分成许多小部分。尽管如此,你仍然无法绕过大转换声明。