Power of Predicates

A secret recipe to spice up your java program

Ever wonder where we can use the predicates ? I was writing a piece of logic which involved classifying data into three groups based on three variables . The code looked nasty and dirty, and I wanted to refactor it in such a way that the whole thing was more maintainable and seamless .

If you consider three variables there are nine different combinations ; of these nine combinations , only a few are valid while others are not.

Flag AFlag BFlag CGroup
YYNGroup C
YNYGroup C
YYYGroup C
NNNGroup B
N NYGroup C
N Y N Group A
Y y NGroup A
N YYGroup C
Y N N Group C
If we are to classify the three lists as Group A , Group B , and Group C , we have to write the

and if this condition becomes bulky , the developer has to run this logic through his head before proceeding to the next line . Readability takes a hit here.

 if (condtion1 || condition2 || condition 3 …) 

We can refactor the code and convert it into a method which returns a boolean , and then again , the developer has to not only go through the code but also scroll down as this extracted method will be found somewhere else.

Testing is done and build passes QA stage, and pre-production stage. You are planning to move this code to production in a couple of days . The client comes up with a priority feature in the same build , which involves adding some more conditions. Along with flag a ,flag b and flag C, there is now a flag X for some cases and flag Y for some cases and flag z for some other cases. Maintainability suffers here.

How comfortable are you to make the change now ? These are critical features and the client doesn’t want the software without these features . We have to do one QA cycle and go through the whole flow again . Test ability is the casualty here.

In short , we have these three problems :
1. Readability
2. Maintainability
3. Ability to test quickly

Functional Programming

Let us try to use functional programming concept here. We have a group A and if an item has be classified in to this group it has to satisfy a condition or condition. Basically we will be working with boolean values. We can use predicates in these scenarios with several advantages .

Predicate is a type of function which takes in an object and returns a boolean value .

@FunctionalInterface
public interface Predicate<T>
Return TypeSignature
1static <T> Predicate<T>isEqual(Object targetRef)
2default Predicate<T>and(Predicate<? super T> other)
3default Predicate<T>negate()
4default Predicate<T>or(Predicate<? super T> other)
5booleantest(T t)
Now, let us build the conditions that are necessary for classifying an object in to a group. All these conditions can be stored as Lists in a class.
List<Predicate<Item>> predicateListA = new ArrayList<>();
predicateListA.add(item->item.getFlagA=='N' &&
                                          item.getFlagB=='Y' &&
                                          item.getFlagC=='N');
predicateListA.add(item->item.getFlagA=='Y' &&
                                          item.getFlagB=='Y' &&
                                          item.getFlagC=='N');

We are now done building the conditions . How do we use them ? We need to combine them. If we see the java docs we will see that there are many methods which we can make use of. and() negate() and or(). For our purpose, we are make use of Or(). This makes our condition re-usable.

predicateListA.get(0).or(predicateListA.get(1)).test()

Even though this looks nice it becomes a hassle to modify this if a new argument is added . So let us clean it up a little, and since we are using using list , we can make use of streams also while we are at it.

groupAClassifier= predicateListA.stream()
                                .reduce((acc,cur)->acc.Or(cur))
                                .orElse(e->true);

This is irrespective of the number of predicates, streams combines it effortlessly. (See references if you want to learn more about streams)

Now that you have predicates stored elsewhere separately, they can be unit tested . Whenever there is an addition / removal of conditions, you just have to run your unit tests and you are good to go live .This makes your code more testable.

Coming back to our example, if we are to group the items into one list , we just have to use the groupAclassifier predicate in the stream . We don’t have to write an extra piece of code for this purpose.

List<Items> groupAItems=allItems.stream()
                                .filter(groupAClassifier)
                                .collect(Collectors.asList());

This addresses all the issues that were seen in our previous approach.

srnyapathi
srnyapathi
Articles: 41