Good bye utility classes, here come builders (part 2)

Good bye utility classes, here come builders (part 2)

This is Part 2 of a 2 parts series on using the builder pattern with Java instead of using utility classes. This part discusses creating ASCII art using a builder. You may start off with Part 1 being on ASCII tables or go on with the article on Base64 encoding with builders.

Do you have some command line Java tools not being enriched with some fancy ASCII art? It’s like Bash scripting and not using FIGlet ;-)

Don't Panic

Don’t Panic, its easy! 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-textual</artifactId>
5     <groupId>org.refcodes</groupId>
6     <version>1.1.7</version>
7   </dependency>
8   ...
9 </dependencies>

Part 2: Building ASCII art

The code snippet for the above DON'T PANIC banner is straight forward: Create and configure your AsciiArtBuilder and print out the text (“DON’T PANIC”):

AsciiArtBuilder theBuilder = new AsciiArtBuilderImpl().
	withFontStyle( FontStyle.BOLD ).withFontType( FontType.SANS_SERIF ).
		withAsciiColorPalette( AsciiColorPalette.MAX_LEVEL_GRAY ).
			withColumnWidth( 120 );
System.out.println( theBuilder.toString( "DON'T PANIC" ) );

Let us do the classic “Hello World!” example by tweaking the above code a little:

Hello World!

Here I changed the font and adjusted the AsciiColorPalette to be used by the algorithm:

AsciiArtBuilder theBuilder = new AsciiArtBuilderImpl().
	withFontStyle( FontStyle.BOLD_ITALIC ).withFontType( FontType.SANS_SERIF ).
		withAsciiColorPalette( AsciiColorPalette.HALFTONE_GRAY ).
			withColumnWidth( 120 );
System.out.println( theBuilder.toString( "HELLO WORLD!" ) );

What about inverting the output?

Hello World! inverted

Here I adjusted the AsciiArtMode

AsciiArtBuilder theBuilder = new AsciiArtBuilderImpl().
	withFontStyle( FontStyle.BOLD_ITALIC ).withFontType( FontType.SANS_SERIF ).
		withAsciiColorPalette( AsciiColorPalette.HALFTONE_GRAY ).
			withColumnWidth( 120 ).
				withAsciiArtMode( AsciiArtMode.INVERSE );
System.out.println( theBuilder.toString( "HELLO WORLD!" ) );

You may even use your own Palette. For the below example I used just tree “colors”:

Hello World! with custom colors

The palette now consists of the three characters ., / and # representing the range from bright to dark:

AsciiArtBuilder theBuilder = new AsciiArtBuilderImpl().
	withFontStyle( FontStyle.BOLD_ITALIC ).withFontType( FontType.SANS_SERIF ).
		withAsciiColors( new char[] {'.', '/', '#'} ).
			withColumnWidth( 120 );
System.out.println( theBuilder.toString( "HELLO WORLD!" ) );

There is another feature the AsciiArtBuilder supports which should not stay unmentioned: Creating ASCII art from images such as .png, .gif or .jpg:

ASCII art from images

You can also pump an image through the AsciiArtBuilder by passing a RgbPixmap from an InputStream:

REFCODES.ORG

Here I used the REFCODES.ORG logo retrieved from an InputStream by the RgbPixmap which is being passed to the AsciiArtBuilder:

InputStream in = new LogoPixmapInputStreamFactoryImpl().createInstance( LogoPixmap.FUNCODES );
RgbPixmap thePixmap = new RgbPixmapImageBuilderImpl().withImageInputStream( in ).toPixmap();
String theLine = new AsciiArtBuilderImpl().
	withPixmapRatioMode( PixmapRatioMode.CONSOLE ).
		withColumnWidth( RuntimeConsts.MIN_CONSOLE_WIDTH ).
			withRgbPixmap( thePixmap ).
				withAsciiColorPalette( AsciiColorPalette.MAX_LEVEL_GRAY ).
					withAsciiArtMode( AsciiArtMode.NORMAL ).toString();
System.out.println( theLine );

Just let the InputStream variable in point to the image source of your choice (.png, .gif or .jpg).

Reflection

Now let us take a a look at and compare the builder pattern with utility classes:

Extensibility

The AsciiArtBuilder can easily be extended without breaking the API. For example we could skip the explicit creation of the RgbPixmap and include the RgbPixmapImageBuilder functionality in the AsciiArtBuilder by adding the methods withImageInputStream(...) and withImageUrl(...)

A utility class on the other hand might provide the following methods in order to provide the same functionality such as that of the AsciiArtBuilder:

public static String[] toAsciiArt( String[] aLines, int aWidth, String aFontName, int aFontStyle, int aFontSize, char[] aPalette, AsciiArtMode aAsciiArtMode ) { ... }
public static String[] toAsciiArt( String[] aLines, int aWidth, java.awt.Font aFont, char[] aPalette, AsciiArtMode aAsciiArtMode ) { ... }
public static String[] toAsciiArt( String aText, int aWidth, String aFontName, int aFontStyle, int aFontSize, char[] aPalette, AsciiArtMode aAsciiArtMode ) { ... }
public static String[] toAsciiArt( String aText, int aWidth, java.awt.Font aFont, char[] aPalette, AsciiArtMode aAsciiArtMode ) { ... }
public static String[] toAsciiArt( RgbPixmap aPixmap, char[] aPalette, AsciiArtMode aAsciiArtMode ) { ... }
public static String[] toAsciiArt( RgbPixmap aPixmap, char[] aPalette ) { ... }
public static String[] toInverseAsciiArt( RgbPixmap aPixmap, char[] aPalette ) { ... }

The number of methods above would even double if we decided to provide the same functionality as above with the difference of returning a single String with embedded line breaks instead of returning String[] arrays where each element of the array represents one line.

For the builder I just have to add the method String toString( String aText ) in addition to the already existing String[] toStrings( String aText ) method, no matter how many properties there are which I can configure for the builder.

Also, extending a utility class’s method with additional parameters would break the API whereas for the builder we just add the additional methods such as - staying with the RgbPixmap example - withImageInputStream(...) and withImageUrl(...) and old code still keeps on working.

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.

Thread safety

A utility class should be thread safe and free of race conditions as of its nature being stateless: All required information is passed to its methods by the according arguments. For the AsciiArtBuilder to be thread safe and free of race conditions I introduced the toString( String aText ) method which avoids the text to be processed to be part of the builder’s state. The configuration attributes are still matter of race conditions, though having configured your builder once, you can invoke its toString( String aText ) method in parallel without the risk of any side effects.

Your builder should provide means to execute its main concern without changing the builder state. Provide an execution method with those parameters to be passed which otherwise would bear a risk for side effects when they were representing state.

I do not consider the configuration properties to be passed as arguments for such an execution method. Though those values which will actually be digested by your builder such as “Hello World!” and “DON’T PANIC” in the above example are to be considered as the main concern: For those properties provide means to execute the builder in a thread safe manner. You may even omit attributes for such arguments!

Resource consumption

When regarding the aspects mentioned above then the creation of builder instances should stay low - in case you do not need to invoke its functionality with ever changing configuration properties.

Readability and usability

For a utility class you might have to remember all the configuration parameters somewhere in your code in order to invoke the utility class methods over and over again. This can bloat your code and make it unreadable and hard to focus on the actual task to be performed by your code. A builder encapsulates these configuration parameters for you.

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.

Utility-builder pattern - a definition

What we came up with looks to me very much like a software design pattern. Let me try a definition in one sentence of what I will call the utility-builder pattern:

The utility-builder pattern is characterized by providing the functionality of a utility class (or parts of it) being implemented with the means of the builder pattern, thereby moving configuration (and the like) concerns (otherwise passed to the utility class’s method as arguments) to the state of the utility-builder instance while providing a thread safe builder method for constructing the utility-builder’s concern which only takes those argument(s) being passed which are directly related to the concern of the utility-builder class and which must not be part of the state of the utility-builder’s instances.

Resources

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

Further reading

comments powered by Disqus