Decorator Design Pattern With Java Lambdas
Today let’s try to implement Decorator design pattern with the help of Java Lambda expressions.
public static void printit(int n, String msg, Function<Integer, Integer> func){
System.out.println(n+" " + msg+" : "+func.apply(n));
}
Suppose we having this method that will do a print output. This accepts Functional interface as an argument. Functional interface is an Interface with a single method in it. The beauty of functional interfaces is it can be used with Lambda expressions to produce awesome results.
How can we execute above method?
printit(5, "printing incremented value .. ", e -> e + 1 );
What we have done here is creating an implementation of the Functional interface to increment the given value by one. To make it more readable let’s rewrite it as
Function<Integer, Integer> incremented = e -> e + 1;
printit(5, "printing incremented value .. ", incremented);
Here we have created incremented class and pass it to the printit method. Still if you aren’t familiar with this lambda expression here it’s in traditional coding.
Function<Integer, Integer> incremented = new Function<Integer, Integer>() {
@Override
public Integer apply(Integer e) {
return e+1;
}
};
printit(5, "printing incremented value ..", incremented);
Hope you are clear with the code what we are doing.
import java.util.function.Function;public class DecoratorDP {
public static void main(String[] args) {
Function<Integer, Integer> incremented = e -> e+1;
printit(5, "incremented", incremented);
}public static void printit(int n, String msg, Function<Integer, Integer> func){
System.out.println(n+" " + msg+" : "+func.apply(n));
}
}
Problem
Function<Integer, Integer> incremented = e -> e+1;
printit(5, "incremented", incremented);
This will increment the given value by one.
Function<Integer, Integer> doubled = e -> e * 2;
printit(5, "doubled", doubled);
This will double the given value.
Now let’s assume we need to increment the value and then double it.
Function<Integer,Integer> incAndDouble = e -> (e + 1) * 2;
printit(5, "incAndDouble", incAndDouble);
Yes this will work and implementation has no issues. But what about code reuse? What if we decided to change the increment logic by two instead of one? we will have to change the code in two places. Code maintenance will be difficult when the code gets complex right?
Solution
Decorator design pattern can help us here!
printit(5,"incAndDouble ", incremented.andThen(doubled));
What have we done here? we have used andThen method of the Functional interface to join two functions! Java framework will apply doubled interface on top of incremented. You can keep on adding more as andThen returns same Functional object.
import java.util.function.Function;public class DecoratorDP {public static void main(String[] args) {
Function<Integer, Integer> incremented = e -> e+1;
printit(5, "incremented", incremented);Function<Integer, Integer> doubled = e -> e * 2;
printit(5, "doubled", doubled);printit(5,"incAndDouble ", incremented.andThen(doubled));}public static void printit(int n, String msg, Function<Integer, Integer> func){
System.out.println(n+" " + msg+" : "+func.apply(n));
}
}
Conclusion
In this code we didn’t duplicate code logic when we need incremented and doubled code together. We have reused same old code logic. That’s the advantage of using this design. It makes the code maintenance easier and also code will be more extensible.
For example what if we need to do double the code first and then increment? (In this example there output will not be changed)
printit(5,"print ... ", doubled.andThen(incremented));
What if we need to increment then doubled and again to increment?
printit(5,"..", incremented.andThen(doubled).andThen(incremented));
Happy coding …
Another Example
import java.awt.*;
import java.util.Optional;
import java.util.function.Function;public class CameraExample {
public static void main(String[] args) {
printit(new Camera(null));
printit(new Camera(Color::brighter));
printit(new Camera(Color::darker));
printit(new Camera(Color::brighter,Color::brighter));
}
public static void printit(Camera camera){
System.out.println(camera.snap(new Color(100,100,100)));
}
}class Camera{
Function<Color, Color> filter;
public Camera(Function<Color,Color>... filters) {
filter = e -> e;
if (filters == null) return; for(Function<Color, Color> aFilter: filters)
filter = filter.andThen(aFilter);
} public Color snap(Color input) {
return filter.apply(input);
}
}
Output will be
java.awt.Color[r=100,g=100,b=100]
java.awt.Color[r=142,g=142,b=142]
java.awt.Color[r=70,g=70,b=70]
java.awt.Color[r=202,g=202,b=202]
In this piece of code we will add filters to the camera to mimic real world world camera filter usage.
printit(new Camera(Color::darker));
This code will create a camera class by passing in the code to make the colors darker. At the camera constructor default class is created by
filter = e -> e;
And then based on the constructor parameters filters are added
filter = filter.andThen(aFilter)
At this time filter to make colors darker is added. Code execution happens at when printit methods invokes.
camera.snap(new Color(100,100,100))
Until this moment we were creating the class do the code execution. At snap method we really pass the parameter and get the output.
As we are using an array of constructors we can add many number of filters to make colors darker or brighter as we done here.
printit(new Camera(Color::brighter,Color::brighter));
This code makes the colors twice brighter.