Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Design pattern for multiple combinations

If I have to make a different database query depending on the presence or not of different parameters, which would be the correct design pattern to avoid too many if-else with the different combinations ? Let's say I have parameters a, b, c (the amount can grow in the future), I'm using repositories so I would have to make a call something like this

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

How can that be better structured ?

like image 282
Arielo Avatar asked Jan 27 '26 20:01

Arielo


1 Answers

At the beginning, I would propose you the Specification design pattern that :

is a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic. The pattern is frequently used in the context of domain-driven design.

but your actual code doesn't suit completely to that as you don't invoke the same method of the repository according to the case.
So I think that you have two ways :

1) Refactoring your repository to provide a single common method accepting a specification parameter and able to handle the different cases.
If you use Spring, you could look at the JpaSpecificationExecutor interface that provides methods such as :

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

Even if you don't use Spring, I think that these examples could help you .

2) If you cannot refactor the repository, you should look for another way and provide a abstraction level about which repository methods/parameters may be passed to.

Actually, you invoke a different method with different parameters according to the input parameters but in any case you return the same type of object to the client of the method : Foo. So to avoid conditional statements, polymorphism is the way to follow.
Each case to handle is finally a different strategy. So you could have a strategy interface and you could determine the strategy to use to return the Foo to the client.

Besides, as suggested in a comment : a!=null && !a.isEmpty() repeated multiple times is not a good smell. It makes much duplication and also makes the code less readable. It would better to apply this processing by using a library such as Apache common or even a custom method.

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

Where FindFooStrategy is defined as :

public interface FindFooStrategy{
  boolean isApplicable(String a, String b, String c);
  Foo getFoo(String a, String b, String c);
}

And where each subclass defines its rules. For example :

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);
  } 
}
like image 100
davidxxx Avatar answered Jan 30 '26 11:01

davidxxx



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!