Hi, I’m Phil and I’m a Java developer at Naimuri with over a decade’s worth of experience. In recent years, Java has changed its tempo of releases. So, I have decided to take some time matching new Java functionality with, appropriate, well established design patterns and algorithms. The goal being to see how they can help in situations that are a little closer to realistic systems than the average examples you get from a Google search. The concept is mostly my own, but Meetups put on by Codurance have certainly helped kick me into gear.
So What Are We Looking At?
The plan, this time, is to pair CompletableFuture with the Gang of Four’s Decorator design pattern, accepting the caveat that each decorator is required to make some type of network call to complete. The question is, can CompletableFuture speed up the rendering of a component wrapped in decorators, preferably without making the pattern illegible!
What Are We Not Looking At?
This is not an in depth analysis of either the Decorator pattern or CompletableFuture. There are plenty of excellent blog posts and articles about that. I’d suggest reading up before continuing, but the short version is that CompletableFuture is an Async Await framework, that allows threads to be spun up to complete certain actions separately to the main thread and only stop and wait for it’s completion where necessary. Decorator is a design pattern that allows you to add layers of decoration to something by employing a clever use of interfaces and delegation.
I took the example in the GoF book as inspiration.
Did I pay attention to the fact that that example was clearly based around some sort of GUI and that it would be a weird thing to include network dependencies in the decorators of? No… Quiet!
The noddy concept is that it decorates a piece of text with line numbers and a text border (2 decorators), which ends up looking like this:
The basic code can be seen here. My only impediment was the fact that Java’s inheritance model is clearly different to that of C++, the language that the GoF book used in its examples. Overall, this was a simple obstacle to overcome.
I contemplated hooking up SQLite to make actual database calls, but decided that was over the top, so my network calls are actually just Thread.sleep() calls, hidden in the Decorator class. 3 seconds seems like a good time… I choose 3 seconds. Using standard synchronous thinking I put said calls right where I needed it, inside the overridden method ‘draw’, of both of my decorators. Unsurprisingly, running my updated code took just over 6 seconds (6060ms). So that’s the number to beat!
Clearly, just slapping CompletableFuture into the same draw method won’t gain you much, if any, time. It also adds some unnecessary complexity to the method. There is little that can be done about this while maintaining the functioning of the design pattern. Decorator relies upon each concrete class calling its parent class, then doing its own work before passing off to its child. However, if we suppose that the results of our network call won’t significantly change between construction of the concrete decorators and when our method is called, we can initialise the CompletableFuture, with ‘CompletableFuture.supplyAsync()’ when it’s constructed and only wait for its results in the draw method and… YES! We have saved almost half the time (3059ms).
Now, as super realistic as this is, it is not perfect. Actual time savings will depend on the exact actions taken by the method passed to CompletableFuture and the order in which the decorators are layered. However, you can see how the total time to execute should not be much longer that the time taken by the most complex decorator.
- Methods passed to CompletableFutures must be completely independent to the rest of the code.
- Significant time savings are available for the right use case of CompletableFutures.
- A sizeable amount of tinkering is required to mould this design pattern into the shape of a useful use case for this feature. Perhaps I should rethink the core concept of these little thought experiments!