4

A Java's web service is intended to be developed that should deal with 10 @QueryParam at least. That is to say, assuming I have 10 query param, I will end up with 90 if-else statements (I think, minimally) trying to construct all possible compilations.

e.g:

public Response getMethod(
        @QueryParam("a") List<String> a,
        @QueryParam("b") List<String> b,
        @QueryParam("c") List<String> c,
        @QueryParam("d") List<String> d,
        @QueryParam("e") List<String> e

       // ... etc
){

 if (a != null && b == null && c == null ...)
 {
    // bla bla bla - 1
 } 

 else if (a != null && b != null && c == null ...) 
 {
    // bla bla bla - 2
 }

....

My question is how to deal with such situations ? i.e. is there any shortcuts / patterns to minimize the work load here ?

SeleM
  • 9,310
  • 5
  • 32
  • 51
  • So you want to check if 4 or more Lists are not null? – Nordiii Feb 11 '20 at 14:40
  • So it is only valid if only one list is given, or why do you check the others for `null`? – daniu Feb 11 '20 at 14:41
  • read up https://stackoverflow.com/questions/494506/dealing-with-nested-if-then-else-nested-switch-statements and https://stackoverflow.com/questions/505454/large-switch-statements-bad-oop for some input how to refactor (nested) conditions using polymorphism and similar techniques. – lugiorgi Feb 11 '20 at 14:42
  • @lugiorgi, not the case, there's no nested if-else in my exp, obviously. – SeleM Feb 11 '20 at 14:46
  • @Nordiii, no, I want to return a set of data according to supplied params. – SeleM Feb 11 '20 at 14:47
  • @selem-mn in the linked question's case it was a nested condition problem but the patterns are _not _ limited to nested conditions! – lugiorgi Feb 11 '20 at 14:48
  • @selemmn You're testing a, b, c in the if case so how does the item count per list also matter – Nordiii Feb 11 '20 at 14:48
  • @Nordiii, it doesnt matter actually the item count per list, what matters is the number of compilations between supplied params (a+b =/= a+b+c =/= a =/= b ...) – SeleM Feb 11 '20 at 14:53
  • @Nordiii I'd updated the question it was a bit misleading – SeleM Feb 11 '20 at 14:58
  • @selemmn (a+b =/= a+b+c =/= a =/= b ...) i guess that means (a+b =/= a+c) ? So its not only number but also which QueryParam and you can not compute them seperatly? – Nordiii Feb 11 '20 at 15:03
  • @Nordiii, yes also (a+b =/= a+c ...) and so on, my bad is that almost all params are eligible to be combined together. I was initially searching for how to reduce the workload in such situations .. how big companies deal with huge filters in their web apps.. – SeleM Feb 11 '20 at 15:09
  • @Nordiii, how come seperatly ? it is one resource that is being knocked in the backend, I do not think it would be a good (even possible) idea in my situation. – SeleM Feb 11 '20 at 15:13
  • 1
    Well I dont know how you're going to process the data after. Are you building an Object with it? Then i would just use Streams and fill the QueryParam specific fields or leave them Empty incase the List is null EDIT: How does the change of an QueryParam change what you do – Nordiii Feb 11 '20 at 15:15
  • 1
    Given all possible permutations, how many different outcomes? –  Feb 11 '20 at 15:15
  • @Nordiii Am getting an object via JPA built-in queries (which are constructed based on supplied params) so I have to know wich params are NULL and which are not. Got a better idea ? – SeleM Feb 11 '20 at 15:22
  • @Andy, different outcomes, not the same queried data when 3 queryParams are supplied VS 4 query params VS 5 VS 9 ... the same POJO but not the same Objects. – SeleM Feb 11 '20 at 15:24
  • So you're building different JPA queries depending on which QueryParam is not null – Nordiii Feb 11 '20 at 15:31
  • @Nordiii EXACTLY ! – SeleM Feb 11 '20 at 15:32
  • 1
    @selemmn Andys approach works fine but maybe the link on this question does give you another solution (in case you are using spring): https://stackoverflow.com/questions/21482339/query-creation-in-spring-data-dynamic-where-clause – Nordiii Feb 11 '20 at 15:56
  • @Nordiii, Many thanks for the approach, I did not know about it before !! – SeleM Feb 11 '20 at 16:03

1 Answers1

2

General idea would be to build a mapping function: input permutations => outcome processor.

To do this use the 10+ boolean conditions (a != null, b != null...) as bit fields to build a 10-bit key.

// assume a,b,c,d...j are in scope and null or not-null
int inK = ((((a != null) ? 1 : 0) << 0 |
            ((b != null) ? 1 : 0) << 1 |
             ...
            ((j != null) ? 1 : 0) << 9))

// so now inK is a 10-bit number where each bit represents whether the
// corresponding parameter is present.

Next...assume a map is built where a single entry's value is an object representing an "outcome processor" for any key. Let's say this object implements OutcomeProcessor.

HashMap<Integer,OutcomeProcessor> ourMap;

Each outcome processor value has a key value which is a mask. So for example a key value of 0x14 would indicate that parameter 'c' (the 4) and 'e' (the 0x10) must be present to invoke the processor.

An OutcomeProcessor would need access to whichever parameters it requires but the interface is general so provide the parameters as a list (of lists in this case) or more generally a collection.

So to build the ourMap, assume you know the mappings and processor implementations:

// first outcome processor (an instance of `OutcomeProcessor1` needs 'a' and 'c' input parameters
ourMap.put(0x5, new OutcomeProcessor1());

// second outcome processor (needs 'a' and 'd')
ourMap.put(0x9, new OutcomeProcessor2());

// so you'll see that if the input is 'a','c' and 'd' then both processors
// are invoked.

// repeat for all possible outcome processors

Next apply the mask to the map

for (Map.Entry<Integer, OutcomeProcessor> entry : ourMap.entrySet()) {
   int mask = entry.getKey();
   if ((mask & inK) == mask) {
      // invoke processor
      entry.getValue().method(<list of parameters>);
   }
}

Pretty simple really.:)

A few properties of this approach:

  • a single instance of the input permutation (e.g. a & f & h present) can (if desired) yield multiple outcomes.
  • a single outcome can be invoked from multiple input permutations (e.g. an outcome processor may require b & c (0x6) and inputs are (a,b,c,d) or (b,c,f) - both input cases may invoked this processor.)
  • you could get more complicated and have the OutcomeProcessor return a result which clears the bits it serviced. And continue looping until all parameters serviced.