Base! How low can you go? Base64, Base32, Base16, ...

Base! How low can you go? Base64, Base32, Base16, ...

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, it can also be used to provide an easy-to-use alternative for implementing utility class functionality.

A good idea is that by default a builder 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 handling Base64 encoding and decoding provides some small example on how to use a builder instead of a utility 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 <dependencies>
2   ...
3   <dependency>
4     <artifactId>refcodes-codec</artifactId>
5     <groupId>org.refcodes</groupId>
6     <version>1.1.8</version>
7   </dependency>
8   ...
9 </dependencies>

Doing Base64 encoding of some binary data with the according BaseCodecBuilder (and its BaseCodecBuilderImpl implementation) is straight forward.

Your bytes are Base64 encoded and the encoded text is stored in a String:

byte[] someBytes = ...;
BaseCodecBuilder theBaseCodeBuilder = new BaseCodecBuilderImpl().
	withDecodedData( someBytes );
String someEncodedText = theBaseCodeBuilder.getEncodedText()

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:

String someEncodedText = ...;
BaseCodecBuilder theBaseCodeBuilder = new BaseCodecBuilderImpl().
	withEncodedText( someEncodedText );
byte[] someBytes =  theBaseCodeBuilder.getDecodedData();

For the same functionality as above the below code is more compact and avoids race conditions regarding the encoded text to be decoded:

String someEncodedText = ...;
BaseCodecBuilder theBaseCodeBuilder = new BaseCodecBuilderImpl();
byte[] someBytes =  theBaseCodeBuilder.toDecodedData( someEncodedText );

Vice versa the other way round, avoiding race conditions for decoded data to be encoded:

byte[] someBytes = ...;
BaseCodecBuilder theBaseCodeBuilder = new BaseCodecBuilderImpl();
String someEncodedText = theBaseCodeBuilder.toEncodedText( someBytes )

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 one Base64 URL applications encoding for URL compatible encoding (using another set of characters valid for URLs).

BaseCodecBuilder theBaseCodeBuilder = new BaseCodecBuilderImpl().
	withBaseCodecMetrics( BaseCodecConfig.BASE64 );
String someEncodedText = theBaseCodeBuilder.toEncodedText( someBytes );

Now specifying the desired encoding does not break the API as the default encoding of the BaseCodecBuilderImpl is the Base64 encoding. If you want to, you may use the URL compatible Base64 URL applications encoding:

BaseCodecBuilder theBaseCodeBuilder = new BaseCodecBuilderImpl().
	withBaseCodecMetrics( BaseCodecConfig.BASE64_URL );
String someEncodedText = theBaseCodeBuilder.toEncodedText( someBytes );

What about a Base16 encoding? Why? Because you can do it!

BaseCodecBuilder theBaseCodeBuilder = new BaseCodecBuilderImpl().
	withBaseCodecMetrics( BaseCodecConfig.BASE16 );
String someEncodedText = theBaseCodeBuilder.toEncodedText( someBytes );

You may even create your own Base16 encoding using the chars of your choice:

BaseCodecMetrics theBase16 =
	new BaseCodecMetricsImpl( 16, NumberBaseConsts.ARABIC_BASE_CHARS );
BaseCodecBuilder theBaseCodeBuilder = new BaseCodecBuilderImpl().
	withBaseCodecMetrics( theBase16 );
String someEncodedText = theBaseCodeBuilder.toEncodedText( someBytes );

The refcodes-codec artifact also provides I/O stream based Base64: The BaseDecodeInputStreamReceiverImpl InputStream and the BaseEncodeOutputStreamSenderImpl OutputStream implementations.

By the way, BaseCodecMetrics should be implemented using the builder pattern as well :-) For some examples see the unit-testsfound here:

Resources

The source codes of the refcodes-codec artifact is found at bitbucket.org.

Further reading

comments powered by Disqus