Describing a problem
Back then in old good Java 7 days, one of my favourite design pattern was Pipeline Pattern. Following this pattern allows me to have my code very well organized. Especially when request processing requires many steps.
Every time when I was forgetting this pattern my code ends like this with business logic hidden somewhere in third layer of processing.
Flat processing structure is something I always appreciate. It is easy to read, flexible and ready for adding new features in process.
Recently I’ve decided to refresh this approach while writing new service. It comes to me that since we have Java 8, there are few more ways to implement pipeline pattern. I will present the four easiest ones:
- classical Pipeline Pattern
- Optional way
- Stream way
- Functional way
Mario Bros Dilemma
Let’s start with some inspiration for writing a simple program. Mario, well known plumber is looking for princess. Most of the time, the only clue he gets is:
Thank You Mario!
But our princess is in another castle!
In my example we will analyze sentence like this:
Princess Francesca is guarded by Dragon
Each plumber: - Mario, - Luigi, - IT Plumber, - Ultimate Plumber
can beat one or more beasts, such as:
- System Administrator
Application will analyze input sentence and decide whether plumber can beat the beast and rescue princess. Expected output will look like this:
or like this:
Old is Gold - Pipeline Pattern
Firstly, we need some glue code. Two simple classes:
Then we can combine first pipeline flow:
The above code is really simple. Plumber quest consists of three steps. Firstly, he should find princess name, then check beast weakness and finally verify whether plumber can beat the beast or not. As you can see there are no Java 8 features in this code. All next three samples are available only in Java 8 world.
Optional gives much more than
get(). Optional API contains some useful methods
which are helpful in simple data transformation such as
filter(). Optional wasn’t designed for solving pipeline pattern
but they are very handy for simple pipes. We just need to wrap input data using Optional with
Optional.of() method and then call
map in every step in the pipe. Second flow looks as following:
Streams, Streams, Streams
Very similar effects gives wrapping all steps using stream with one element. Stream API is well known for everyone who writes anything in Java 8.
Stream looks nice, but last step - getting result data - is quite lame. We have to call
findFirst() which returns Optional and
get() at the end. The other way is to call
take first element of result list.
Functions for purists
Last approach uses
Function API. The truth is that functions were used under the hood in Optional and Stream examples.
Right now we will use function explicitly. Here we go:
As you can see there are more than one way to implement pipes in Java 8. You can pick those you like the most.
A few weeks ago I started learning RxJava. I realized that RxJava is the modern Pipeline Pattern. It is much more easy to dive into world of Observables when you followed Pipeline Pattern in the past. What is more, it is really easy to rewrite code into Rx. Last but not least: I found out that it is quite a good approach (when you are not RxNinja) firstly to write Java 8 pipes and then to refactor code with usage of Rx streams.