Take a step back in history with the archives of PragPub magazine. The Pragmatic Programmers hope you’ll find that learning about the past can help you make better decisions for the future.

FROM THE ARCHIVES OF PRAGPUB MAGAZINE OCTOBER 2017

Refactoring to Functional Style in Java 8: Applying the Decorator Pattern

By Venkat Subramaniam

PragPub
The Pragmatic Programmers
8 min readAug 29, 2022

--

All this year, Venkat is exploring how to exploit the benefits of functional style in Java. In this installment of his series, he looks at how a functional approach can improve a particular design pattern.

https://pragprog.com/newsletter/
https://pragprog.com/newsletter/

Java programmers have a special liking for design patterns. While some patterns are easy to implement, a few patterns turn into a lot of code. In this article we will look at one such pattern, the Decorator pattern, that turns ironically into ugly and verbose code when traditional approaches are used in Java. We will then refactor that code to use functional interfaces, lambda expressions, and method references, to make it concise and elegant.

Decorator Using Traditional Code

The Decorator pattern is a popular design pattern presented in the book Design Patterns: Elements of Reusable Object-Oriented Software. The pattern is intended to create extensible code where we can flexibly add functionality to existing objects.

You may have used the good old FilterInputStream, BufferedInputStream, DataInputStream, … of the java.io package. You may also recollect how verbose the code got when we combined instances of these classes. That is an example of the use of Decorator pattern in the JDK.

Let’s explore using the Decorator pattern with an example, using traditional Java code.

Suppose we have a Signal class that holds a magnitude value.

public class Signal {
private int magnitude;
public Signal(int theMagnitude) {
magnitude = theMagnitude;
}
public int getMagnitude() {
return magnitude;
}
}

The hypothetical application at hand may want to perform different transformations on the magnitude of signal. To easily create combination of transformations, we may use the Decorator pattern that facilitates combining operations.

Let’s create an abstract class named SignalTransformer for this purpose, like so:

public abstract class SignalTransformer {
private SignalTransformer next;
public SignalTransformer() {
}
public SignalTransformer(SignalTransformer nextTransformer) {
next = nextTransformer;
}
public Signal transform(Signal signal) {
if(next != null) {
return next.transform(signal);
}
return signal;
}
}

The SignalTransformer class has two constructors: the first initializes the next reference to the default null value while the second constructor initializes it to the given SignalTransformer instance. In the transform function, if the next reference refers to a next transformer, the given signal is transformed using that instance. If not, the given signal is merely returned.

Let’s look at a class that extends SignalTransformer to augment the magnitude of a given signal:

public class SignalAugmenter extends SignalTransformer {
public SignalAugmenter() {}
public SignalAugmenter(SignalTransformer next) { super(next); }
public Signal transform(Signal signal) {
return super.transform(new Signal(signal.getMagnitude() + 1));
}
}

The SignalAugmenter has two constructors corresponding to the constructors of its base class. In the transform method it creates a new Signal instance with magnitude value one more than that of the given Signal.

Likewise, we may create a SignalDamper that creates a signal with a dampened magnitude:

public class SignalDamper extends SignalTransformer {
public SignalDamper() {}
public SignalDamper(SignalTransformer next) { super(next); }
public Signal transform(Signal signal) {
return super.transform(new Signal(signal.getMagnitude() — 1));
}
}

Finally, let’s consider a signal multiplier:

public class SignalMultiplier extends SignalTransformer {
private int factor;
public SignalMultiplier(int multiplicationFactor) {
factor = multiplicationFactor;
}
public SignalMultiplier(int multiplicationFactor,
SignalTransformer next)
super(next);
factor = multiplicationFactor;
}
public Signal transform(Signal signal) {
return super.transform(new Signal(signal.getMagnitude()
* factor));
}
}

The constructors in this class receive a multiplication factor and use that within the transform method to multiply the magnitude by that value.

For each transformation we want to write, we may have to create a couple of constructors and a method — that’s traditional object-oriented code to implement the pattern.

Now, let’s see some examples of using these transformations. Here’s a

UseTransformers class with an applyTransformation and a main method:

public class UseTransformers {
public static void applyTransformation(Signal signal,
SignalTransformer signalTransformer) {
System.out.println(“Given signal’s magnitude: “ +
signal.getMagnitude());
system.out.println(“Magnitude after transformation: “ +
signalTransformer.transform(signal).getMagnitude());
}
public static void main(String[] args) {
Signal signal = new Signal(10);
applyTransformation(signal, new SignalAugmenter());
}
}

In the applyTransformation method we receive an instance of Signal and a SignalTransformer, print out the signal magnitude, then transform the signal using the given SignalTransformer, and print out the magnitude of the transformed signal.

In main we use SignalAugmenter to transform a sample signal. That was simple and easy to write. The output from this call shows the signal magnitude increase:

Given signal’s magnitude: 10 
Magnitude after transformation: 11

The effort to use either of the other two transformers we wrote is similar:

applyTransformation(signal, new SignalDamper()); applyTransformation(signal, new SignalMultiplier(2));

Here’s the output of the original signal transformed using these two transformations:

Given signal’s magnitude: 10 Magnitude after transformation: 9 
Given signal’s magnitude: 10 Magnitude after transformation: 20

It gets noisy when we want to combine transformations. Let’s transform the sample signal using the multiplier and then augment it. The code for this, using the traditional Decorator pattern, is:

applyTransformation(signal,
new SignalMultiplier(2, new SignalAugmenter()));

We created an instance of the augmenter and passed it into the constructor of the multiplier. Here’s the output of transforming the original signal through these two combined transformers:

Given signal’s magnitude: 10 
Magnitude after transformation: 21

We used new twice and used two constructors, but it can get quite verbose. What if we want to combine a signal damper transformer with a multiplier, and then an augmenter? Here’s the code:

applyTransformation(signal,
new SignalDamper(new SignalMultiplier(3, new SignalAugmenter())));

It feels like the signal is getting lost in the noise, so to speak. There has to be a simpler and more elegant solution. Thankfully there is, in Java 8.

Composing Functions

Let’s take a short detour from the problem at hand to visit the Function functional interface in JDK 8.

This interface is useful to transform a value or object into another. The map method of Stream makes use of this, but it may be used in other contexts as well.

Let’s write a method that takes an integer value and applies a given Function, like so:

public static void applyFunction(int value, String message, 
Function<Integer, Integer> mapper) {
System.out.println(value + “ “ + message + “: “ +
mapper.apply(value));
}

Here’s an example of using the applyFunction method:

Function<Integer, Integer> increment = value -> value + 1; applyFunction(5, “incremented”, increment);

The increment function, or lambda expression, takes a value and returns the value incremented by one. When applyFunction is called with a value of 5 and the increment function, the result is:

5 incremented: 6

We can similarly create another such function and use applyFunction, like so:

Function<Integer, Integer> square = value -> value * value; 
applyFunction(5, “squared”, square);

What if we want to perform a combined operation, for example, increment and then square? The applyFunction receives only one Function, we can’t change that. We don’t want to duplicate the code for increment or for the square operation on the caller side. But we want a Function that will perform increment and then perform the square operation. That’s exactly what we’ll do, thanks to the andThen method which is a default method in Function.

applyFunction(5, “incremented and squared”, increment.andThen(square));

That’s pretty concise, no noise, no ceremony, just the essence.

The andThen method creates a Function that will perform the mapping function to the left of andThen first and pass the result of that operation to the mapping function given as argument to the andThen method.

We can use the andThen method to create a highly expressive and easy-to-use decorator pattern implementation.

Decorator with Function Composition

For the signal transformation problem we wrote one abstract base class to chain operations, one verbose class for each transformation, and a series of object instantiation to combine transformations. We can do a lot better with the Function functional interface.

The Signal class stays the same as before; that’s the only thing we will keep as is. Let’s get rid of SignalTransformer, SignalAugmenter, SignalDamper, and SignalMultiplier. Instead, we’ll create a new interface, SignalTransformers, with simple static methods for each transformation:

public interface SignalTransformers {
public static Signal augment(Signal signal) {
return new Signal(signal.getMagnitude() + 1);
}
public static Signal dampen(Signal signal) {
return new Signal(signal.getMagnitude() — 1);
}
public static Signal multiply(Signal signal,
int multiplicationFactor) {
return new Signal(signal.getMagnitude() * multiplicationFactor);
}
}

We chose interface instead of an abstract class or a regular class for SignalTransformers because we will not extend from it nor we will ever attempt to create an instance.

The signal augmenter turned into a simple method instead of a class. Likewise, instead of two constructors and a method, the signal multiplier is also a nice little method.

Let’s see how to use this newly created interface.

We will rewrite the applyTransformation function of UseTransformers, like so:

public static void applyTransformation(Signal signal, 
Function<Signal, Signal> signalTransformer) {
System.out.println(“Given signal’s magnitude: “ +
signal.getMagnitude());
System.out.println(“Magnitude after transformation: “ +
signalTransformer.apply(signal).getMagnitude());
}

Instead of receiving a SignalTransformer, the second parameter is a Function<Signal, Signal>. Within the applyTransformation method, we transform the signal using the apply method of Function.

Let’s modify the main method to use the applyTransformation. First we’ll create a handful of transformation functions, using the static methods in SignalTransformers interface.

public static void main(String[] args) { 
Function<Signal, Signal> augmentMagnitude =
SignalTransformers::augment;
Function<Signal, Signal> dampenMagnitude =
SignalTransformers::dampen;
Function<Signal, Signal> doubleMagnitude =
signalToTransform ->
SignalTransformers.multiply(signalToTransform, 2);
Function<Signal, Signal> tripleMagnitude =
signalToTransform ->
signalTransformers.multiply(signalToTransform, 3);
//…
}

The first Function, augmentMagnitude, is created using a method reference to the augment method in SignalTransformers. The second Function is very similar. The doubleMagnitude is created using a lambda expression whose body invokes the multiply method of SignalTransformers with the appropriate multiplier value. The tripleMagnitude is similar to the doubleMagnitude.

We may make use of these Functions to perform the transformation quite easily. First let’s use each of the transformations individually:

Signal signal = new Signal(10); 
applyTransformation(signal, augmentMagnitude);
applyTransformation(signal, dampenMagnitude);
applyTransformation(signal, doubleMagnitude);

We passed some of the Functions we created to the applyTransformation method. Recollect that creating a combined augmenter and multiplier or a combined damper, multiplier, and augmenter was a noisy:

//old style
applyTransformation(signal, new SignalMultiplier(2,
new SignalAugmenter()));
applyTransformation(signal,
new SignalDamper(new SignalMultiplier(3,
new SignalAugmenter())));

That same operation now becomes:

//new version
applyTransformation(signal,
doubleMagnitude.andThen(SignalTransformers::augment));
applyTransformation(signal, dampenMagnitude.andThen(tripleMagnitude).andThen(augmentMagnitude));

The composition dampenMagnitude.andThen(tripleMagnitude).andThen(augmentMagnitude)is concise, expressive, easy to read, and to modify as well.

The overall code for the example went from 79 lines to 48 lines. More than the reduction in number of lines, the code to combine transformation has seen significant improvement.

Result: an implementation of the decorator pattern that is beautiful, reflecting the esthetic connotation of the pattern’s name.

Conclusion

The decorator pattern is a very popular design pattern, yet its implementation has been overly noisy and verbose. The noise largely came from creating classes for each operation and instances needed to combine the operations. With lambda expressions, method references, and the ease with which we may combine Functions, we are able to refactor the pattern implementation. The result is a highly concise, elegant, and expressive code that is easier to understand and maintain.

About Venkat Subramaniam

Dr. Venkat Subramaniam is an award-winning author, founder of Agile Developer, Inc., and an instructional professor at the University of Houston. He has trained and mentored thousands of software developers in the U.S., Canada, Europe, and Asia, and is a regularly invited speaker at several international conferences. Venkat helps his clients effectively apply and succeed with agile practices on their software projects. Venkat is a (co)author of multiple books, including the 2007 Jolt Productivity award-winning book Practices of an Agile Developer.

Cover from PragPub Magazine, October 2017 featuring a seated person dressed as the Grim Reaper.
Cover from PragPub Magazine, October 2017

--

--

PragPub
The Pragmatic Programmers

The Pragmatic Programmers bring you archives from PragPub, a magazine on web and mobile development (by editor Michael Swaine, of Dr. Dobb’s Journal fame).