Functional Patterns in Java

How to use different design patterns with functional programming in Java

The Bored Dev
Jul 5 · 12 min read
Image for post
Image for post
Photo by Adrian Swancar on Unsplash

Recently we started a journey through functional programming in Java with my articles “A new functional Java style” and “Mastering the new functional Java.”

Now that we have a basic understanding about functional programming and what Java brings to us, I think it’s time to see how we can apply this in order to improve our existing code.

The introduction of functional programming in Java has brought new possibilities to the way we write code. We now have much more flexibility.
We’ll be going through different common design patterns and showing how functional programming helps improve them — making them less verbose, more readable, and more maintainable.

Let’s start then!

Introduction

A design pattern is a solution we normally apply to solve a common problem, following some best-practice guidelines.

Their main purpose is normally to improve the maintainability and readability of the code, guaranteeing that some known issues will be avoided if we follow a given pattern.

There are multiple design patterns, and normally they’re divided into different groups:

Behavioral patterns

These identify common communication patterns among objects. Some of the most popular behavioral patterns are strategy, visitor, chain of responsibility, the template method, observer, iterator, etc.

Creational patterns

These involve different mechanisms for objects creation. The most popular ones are factory, builder, prototype, the factory method, etc.

Structural patterns

These identify ways of composing objects to make enhanced objects that serve a purpose. Some of them are adapter, bridge, proxy, decorator, etc.

We’re not going to go into detail for all of them because that’d take a long time; instead we’re going to pick some of the most important patterns and see the way we used to implement them and how we can improve them now by making use of functional programming.

The Factory Method Pattern

In the factory method pattern we provide an interface to the client to create an instance of the object, hiding the implementation details on how to create the objects.

Before Java introduced functional programming, this pattern could be implemented just by using if conditions, a switch case or even a . (please avoid switch statements, by the way). My favorite implementation was the one that uses Java’s enum; however, this isn’t always possible if we need to inject a dependency into the component we’re creating.

Let’s take a look at an implementation of this pattern in a nonfunctional way:

Image for post
Image for post

As we can see, our factory method accepts the type we want to instantiate and also a VehicleColor, which will be used to instantiate a vehicle of that color.
How would we use our implementation from a hypothetical client?

Vehicle redCar = VehicleFactory.instanceOfType(VehicleType.CAR, VehicleColor.RED);

It doesn’t look that bad, but what are the issues with this implementation?

  • The use of if conditions to check type normally leads to code duplication
  • Adding a new type implies having to remember that we have to add a new if condition
  • We have to throw IllegalArgumentException, as it’s possible that we’ll forget to handle a new type
  • It’s harder to read than the functional approach, as we’ll see shortly
  • It’s slightly more verbose than the functional approach

So how can we implement this in a cleaner way, taking advantage of Java functions? Let’s take a look at this:

Image for post
Image for post

You’ll notice we’ve reused the existing VehicleType enum, and we’ve added a factory function to it; in our example. we pass the method references for each of the constructors of our implementations. At first sight, we can quickly notice that this class is cleaner and easier to read, reducing the clutter required just to create an instance of a given type.

Also, every element in the enum is forced to implement its factory method, so it’s impossible that we’ll forget to add a factory for a recently added type! The Java compiler helps us here, guiding us to write the required changes when a new type is added; no additional mental effort has to be done from our side.

Let’s see now how can we use this implementation from a client:

Vehicle redCar = VehicleType.CAR.factory.apply(VehicleColor.RED);

Quite simple, right? As we’ve seen, this implementation has a few advantages over the nonfunctional approach. I really hope you like it and start using it frequently.

The Template Method Pattern

The template method is a pattern that allows us to define some common steps for an algorithm. Then, the subclasses override some of these steps with their specific behaviors for a particular step.

So how did we previously write this pattern in Java? Well, most of the time, we had to use an abstract class and had to define abstract methods for each substep that could be overridden. I have to admit I used to do this in the past, probably because it was the only way, but now I see it as a very bad way of doing this.

Let’s see an example of how it used to be:

Image for post
Image for post

This is a very simple example, but basically each vehicle will print what initial check it’ll have to do before starting; in order to do that, each subclass will have to override the preStartCheck abstract method. For example, if I modify the class to extend AbstractVehicle, it’ll force me to override the preStartCheck method.

For example, these steps for the bus could be:

@Override 
void preStartCheck() {
System.out.println("Check if every new passenger has paid for their tickets");
System.out.println("Check if every passenger is seated");
}

This way isn’t ideal, as we have to create an abstract class and each of our subclasses have to extend this abstract class. It’s messy, hard to follow, and adds a considerable amount of clutter disproportionate to what it actually does. As a rule of a thumb, I’d advise to avoid extending classes in Java — it’ll make your life easier in the long term.

So what’s the alternative? Let’s move our start method to the interface, and we’ll make use of the default modifier to provide a default implementation of our method in the interface.

Image for post
Image for post

As you can see, we now accept a Consumer to provide the preStartChecks for our class. This is a very silly example, so some things will look a bit strange, like the use of a Consumer of Void. Don’t worry too much about it at the moment — this is just to exemplify how can we inject behavior using Java functions.

So once we have that, how can we inject our preStartChecks’ logic? Let’s take a look:

Image for post
Image for post

And if we run our code, it’ll print what we expect:

Check if every new passenger has paid for their tickets 
Check if every passenger is seated
Bus starting...

So that’s basically it, we’ve achieved exactly the same behavior just by writing two lines in our interface. Previously, we needed an abstract class and an abstract method, and each of our classes was being forced to implement the abstract methods. This solution is much simpler, cleaner, and more readable — don’t you agree? I hope you do!

The Builder Pattern

The main purpose of the builder pattern is to provide a way of constructing an object in steps, separating the construction logic from its representation.

The nonfunctional way of creating a builder — while being very robust and very easy to use by a client — has a problem: it’s very verbose.

So how can a functional approach help us in this case? By using a functional approach, we’ll see how can we drastically reduce the amount of code needed to implement a builder.

Let’s start first by looking at how it used to look like before; you’ll probably like its expressiveness from a client’s perspective, but you won’t like its verbosity. We’re going to recover the example in my article “Please stop the Java Optional mess!” and see how can we transform it into a functional style.

If you remember from that article, using that class from a client was quite easy; we could, for example, do something like this:

That’s quite nice, isn’t it? So our only problem in this case is the verbosity of the class — let’s see how functional Java can help.

First of all, the builder embedded in our Customer class will be transformed to something like this:

Image for post
Image for post

As you can see, now we accept a Consumer— and we only provide one method, instead of multiple methods! This is quite beneficial if our object has many fields.

Let’s take a look now at how we could use this implementation from a client’s perspective:

Image for post
Image for post

That’s brilliant, right? We can specify all of our fields in one single Consumer instead, making our code more concise.

Let’s move on now to a different pattern: strategy!

The Strategy Pattern

The strategy pattern is probably one of the most widely used design patterns; it’s normally used in every situation where we have to choose a different behavior based on some property or input.

The only problem with the old way of writing a strategy pattern is that in many cases we have to create a lot of classes and boilerplate code; this could make sense when these classes hold complex logic, but in many cases, they’re too simple to even become a class!

Image for post
Image for post
Photo by Kaleidico on Unsplash

It’ll be easier to see this with an example; in this example, we’ll build a DeliveryPriceCalculator, which will depend on the plan the customer has paid for.

First of all, these are the existing plans:

Image for post
Image for post

Based on the existing plans, we’ll have to create an implementation for each plan; so we’ll need a DeliveryPriceCalculator interface and three implementations of this interface:

Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post
Image for post

The implementations are quite simple. In some of them, the customer will be charged a small percentage of the item price plus one dollar; for those customers paying for the business plan, the delivery price will be just $1.

So what do we need in order to instantiate the class we need? We need a factory, which has been implemented in the same old-style way we showed earlier when we looked at the builder pattern; therefore, we’ll omit its implementation. The code for this example can be found here in my GitHub account, in case you need it.

Let’s look at how the client will be using this then:

So we can see, again, that the main problem with this pattern is its verbosity — the unnecessary clutter we’re creating for something so simple. Do we really need all those classes? Can’t we make this simpler and more concise?
Let’s see how functional programming can come to the rescue here!

Instead of creating a new class for each implementation, we’re going to embed all this logic using Java functions.

Image for post
Image for post

Please notice that we only need an enum to implement the whole strategy pattern in this case. Every plan is forced to implement its own delivery price calculator by providing a function. That looks quite simple and concise to me, don’t you agree?

Also, the way the client uses this pattern has become simpler:

Just one line — and it’s really expressive now! I really hope you like this pattern; in my opinion, it’s quite obvious it helps in reducing clutter and boilerplate code in our Java code.

Let’s take a look now at one last example: the chain-of-responsibility pattern!

The Chain-of-Responsibility Pattern

This pattern is very useful when we have to do different operations on an object in a given order, like we do in a chain factory.

The nonfunctional way of implementing this pattern required creating different classes, in a similar way to the strategy pattern. This used to be a bit verbose and added unnecessary complexity to our code.

How does it look like using nonfunctional programming?

I have to say beforehand that getting the chain-of-responsibility pattern right is hard using a nonfunctional approach; our example is very very simple, but still it’s very error-prone and difficult to get right. What do we need in order to implement this pattern?

We’re going to implement what I think is a good example for this pattern: a car wash machine. First of all we need an abstract class. Yeah, sorry … we need an abstract class and all the subclasses need to extend it. Remember to avoid this kind of situation in the future in Java — it doesn’t bring anything good!

Image for post
Image for post

Now, for the sake of brevity, I’ll include just one of the implementations to show how each of them would look like:

Image for post
Image for post

As you can see, in every step in our chain, we’ll call if there’s a next step; when it reaches the end of the chain, it’ll stop there. How would we use this from our client?

If we execute this code, we’ll see each of the steps being executed:

Car state transitioned to INITIAL 
Car state transitioned to INITIAL_WASH
Car state transitioned to SOAP
Car state transitioned to RINSED
Car state transitioned to POLISHED
Car state transitioned to DRIED
Final car state is DRIED

So this implementation works, but it’s quite complex and verbose. Let’s see how functional programming can help here!

You’re going to be very impressed with how much code we can save with this approach.

Image for post
Image for post

That’s it! Impressive, right? In this example we can make use of the andThen method in the Java Function and create a chain of functions using only Java built-in functions. There’s no need for any custom classes, actually. We’ve reduced our pattern to a few lines, and now it’s also much more difficult to get it wrong.

One improvement we should do is to extract those functions to a method — in that way, you can express what that function is responsible for. For example, we could have a soapStep method that creates that function.

So after applying that refactoring, our chain of functions would look like this:

Image for post
Image for post

That’s much better, right? Now our chain of functions is easier to read, more meaningful, and in those cases where our functions have complex logic, they can be easily unit tested.

So that’s it! I hope you’ve liked our examples and you’ve enjoyed applying functional programming to achieve more concise and readable code.

If you still struggle with understanding Java Lambdas and functional programming in general, I’d recommend that you read “Functional Programming in Java: Harnessing the Power Of Java 8 Lambda Expressions”; you can buy it on Amazon in the following link.

Also, please remember you can find all these examples on GitHub here.

Conclusion

Functional programming is a big jump from an imperative approach. I’ve also been there before, and I know it takes time to assimilate concepts and adapt our coding skills to this new approach; however, once you have a good command of it, you’ll see all the benefits it can bring to your code and how it can increase your productivity.

Many of Java’s past detractors have pointed to the verbosity of the language and all the boilerplate code and clutter that needed to be generated; I actually think they were right in many ways, but I also think that recent changes in Java are starting to close that gap, as we’ve seen in these examples.

Personally, I see a bright future ahead for Java if they keep bringing these kind of changes, allowing us to adapt our code to these new times.

Image for post
Image for post
Photo by Greg Rakozy on Unsplash

So that’s all from me! I really hope you’ve enjoyed reading this article.
I hope I see you again soon.

Thanks you very much for reading!

Better Programming

Advice for programmers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app