I don’t like utility classes. Somehow they seem to fill up with functionality soon forgotten and never to be found again. While refactoring lots of utility classes I came across some Base64 related functionality in one of them which delegated its functionality to some third party API …
Some words on utilities and the builder pattern
When filling up utility classes
with functionality, some such methods
seem to be cluttered with arguments
to make the desired functionality really generic. Which in turn makes the maintenance of such methods error prone and their use tiresome:
Which arguments are mandatory? Which arguments are optional? Which are the best default values for optional arguments?
Sometimes those utility classes
end up with the same method
implemented various times with differing sets of arguments passed. Just to cover any possible and impossible usage scenario.
Although the builder pattern
usually is used to avoid the telescoping constructor
anti-pattern
, a weak variant of the builder pattern can also be used to provide an easy-to-use alternative for implementing utility class
functionality.
In this context, a weak builder
or a weak variant of the builder pattern is considered to combine the builder pattern with the subject of interest into a single object, e.g. the actual functionality is enriched with builder
functionality instead of separating the builder
functionality from the functionality of interest.
A good idea is that by default a weak builder
(also referred as builder
in this context) replacing a utility class
provides a sound default configuration. One might speak of convention over configuration in this context as well. The developer just adjusts those properties to actually be adjusted for her own needs.
A nice side effect of such a builder
is that you easily find it with the type search of your IDE
as one builder
implements one specific functionality and as its name reflects its functionality. As one builder
implements one specific functionality, it is easily maintained and extended without resulting in hundreds of lines of complex code and dozens of telescoping methods. Moreover, extending the builder
with new functionality does not break your API
as you would do by extending a method
of a utility class
with additional arguments.
A caveat on using builders
instead of utility classes
could be the additional memory and CPU
usage required as a builder
got to be instantiated before usage, whereas invoking functionality of a utility class
does not require any instantiation. Though once configured, a builder
reliefs you from the need to carry around all arguments in your code for invoking the builder
functionality (as you would have to do when using utility classes
).
Enough said, the
codec
handlingBase64
encoding and decoding provides some small example on how to use abuilder
instead of autility class
Base64 encoding and decoding
Let’s take a look at some basic encoding and decoding example using a builder
. To get up and running, include the following dependency (without the three dots “…”) in your pom.xml
:
1
2
3
4
5
6
7
8
9
<dependencies>
...
<dependency>
<artifactId>refcodes-codec</artifactId>
<groupId>org.refcodes</groupId>
<version>3.3.8</version>
</dependency>
...
</dependencies>
Doing Base64
encoding of some binary data with the according BaseBuilder
is straight forward.
Your bytes are Base64
encoded and the encoded text is stored in a String
:
Doing Base64
decoding of some binary data is as simple as encoding. Your text is Base64
encoded and the decoded binary data is stored in a byte
array:
For the same functionality as above the below code is more compact and avoids race conditions
regarding the encoded text to be decoded:
Vice versa the other way round, avoiding race conditions
for decoded data to be encoded:
So where is the
builder
pattern?
Base! How low can you go?
Now I was on fire, why not also providing Base32
, Base16
, … , Base2
encoding and decoding functionality? I don’t think that it is of any serious use to have other encodings than Base64
, but it was some kind of puzzle I was keen to solve, just for fun. So I designed the builder
with other encodings in mind than just Base64
encodings.
Did you actually know that there are various
Base64
encoding formats? Yes, there is oneBase64 URL applications
encoding forURL
compatible encoding (using another set of characters valid forURLs
).
Now specifying the desired encoding does not break the API
as the default encoding of the BaseBuilder
is the Base64
encoding. If you want to, you may use the URL
compatible Base64 URL applications
encoding:
What about a Base16
encoding? Why? Because you can do it!
You may even create your own Base16
encoding using the chars of your choice:
The
refcodes-codec
artifact also provides I/O stream basedBase64
: TheBaseInputStreamDecoder
InputStream
and theBaseOutputStreamEncoder
OutputStream
implementations.
By the way, BaseMetrics
should be implemented using the builder pattern
as well :-) For some examples see the unit-tests
found here:
Resources
The source codes of the refcodes-codec
artifact is found at bitbucket.org
.