Writing clean predicates in Java 8

Using an expression language for predicates, like we have done before, is fun! But it’s a pain to maintain :).

Now that we finally (I hope) have a good release date for Java 8 (supporting lambda -styled predicates and streams), it’s time to think how we are going to re-organize our predicates. Let’s review our predicates for Java 8:

Inline expressions create a maintenance nightmare

Writing in-line lambda expressions and using the stream interfaces to perform common operations on collections is fun and easy. Assume the following example:

List<Person> getAdultMales (List<Person> persons) {
  return persons.stream().filter(p -> 
    p.getAge() > ADULT &&
    P.getSex() == SexEnum.MALE 
  ).collect(Collectors.<Person>toList());
}

That’s fun! But things like this also lead to software that is costly to maintain. At least in an enterprise application, where most of your code handles business logic, your development team will grow the tenancy to write the same similar set of predicate rules again and again. That is not what you want on your project.  It breaks three important principles for growing maintainable and stable enterprise applications:

  • DRY (don’t repeat yourself): writing code more than once is not a good fit for a lazy developer ;) It also makes your software more difficult to maintain because it becomes harder to make your business logic consistent
  • Readability: following clean-code best practices, 80% of writing code is reading the code that already exists. Having complicated lambda expressions is still a bit hard to read compared to a simple one-line statement.
  • Testability: your business logic needs to be well-tested. It is adviced to unit-test your complex predicates. And that is just much easier to do when you separate your business predicate from your operational code.

And from a personal point of view… that method still contains too much boilerplate code…

Imports to the rescue!

Fortunately, we have a very good suggestion in the world of unit testing on how we could improve on this.

Imagine the following example:

import static somepackage.PersonPredicate;
...
public Persons {

private List<Person> persons;
List<Person> getAdultMales () {
  return persons.stream().filter(
    isAdultMale()
  ).collect(Collectors.<Person>toList());
}

We have a “Persons” group, containing a list of Person domain objects.

What we did here was:

  • create a PersonPredicate class
  • define a “factory” method that creates the lambda predicate for us
  • statically import the factory method into our old class

This is how such a predicate class could look like, located next to your Person domain entity:

public PersonPredicate {
  public static Predicate<Person> isAdultMale() {
    return p -> 
      p.getAge() > ADULT &&
      p.getSex() == SexEnum.MALE; 
 }
}

Wait… why don’t we just create a “isMaleAdult” boolean function on the Person class itself like we would do in domain driven development? I agreed, that is also an option… but as time goes on and your software project becomes bigger and loaded with functionality and data… you will again break your clean code principles:

  • the class becomes bloated with all kind of function and conditions
  • your class and tests become huge, more difficult to handle and change (*)

(*) and yes… even if you do your best to separate your concerns and use composition patterns

Adding some defaults…

Working with domain objects, we can imagine that some operations (such as filter) are often executed on domain entities. Taking that into account, it would make some sense to let our entities implement some interface that offers us some default methods.

For example:

public interface DomainOperations {
    default <T> List<T> filter (List<T> data, Predicate<T> predicate) {
        return data.stream().filter( predicate )
               .collect(Collectors.<T>toList());
    }
}

When our Persons entity implementing this interface, we can clean-up our code even more. Of course, this is just adding some syntactic sugar for now. But along the way, more complicated operations can be imagined.

public Persons implements DomainOperations {

  private List<Person> persons;

  List<Person> getAdultMales () {
    return this.<Person>filter( persons, isAdultMale() );
  }

}

And there we go…

Conclusion

Moving your predicates to a Predicate helper class offers some good advantages in the long run:

  • Predicate classes are easy to test and change
  • Your domain objects remain clean and focussed on representing your domain, not your business logic
  • You optimize the re-usability of your code and, in the end, reduce your maintenance
  • You seperate your business from operational concerns

References

Clean Code: A Handbook of Agile Software Craftsmanship [Robert C. Martin]

Practical Unit Testing with JUnit and Mockito [Tomek Kaczanowski]

State of the Collections [http://cr.openjdk.java.net/~briangoetz/lambda/collections-overview.html]

About these ads

4 thoughts on “Writing clean predicates in Java 8

  1. Hi

    I am just starting with JAVA 8 but to me your code from ADDING SOME DEFAULTS… section is simply wrong: what is person variable in DomainOperations interface ? So i think the filter method must take List of T and Predicate of T as arguments.

    • Hi!

      I am very sorry about not proof-reading my post correctly :). The last code snipsets where completely not OK.

      I adapted the code a bit to correct these errors:
      - Yes, we do need to get the collection (list here in this example) as an argument
      - Having the generic type on the interface was also not very good, because it would force all defaults on to use the same type
      - There where some copy-patse typo’s in the text (like explicitly refering the Person class). Goes to show that you better copy-paste code written from an IDE into this blog instead of manually typing it in the text editor.

      Finally, please note that in order to properly use the defaults, the interface should be implemented by a container (owning the Person class for example). I updated the code a little bit to reflect this.

      Enjoy playing with Java8!

  2. If we continue to reasons with the same manner, will get anemic domain model. I think we should put “isMaleAdult” boolean method on the Person class. We did it and the code is really clean (just when you do this you should put in mind DDD bounded context)

    • sure. it was not the purpose of the example, but isMaleAdult would be better in a limited context… or even iAdult && isMale, depending on how you design your domain.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s