This is part 2 of a two parts series on using a weak variant of 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.
Part 2: ASCII art
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.
Do you have some command line Java tools not being enriched with some fancy
ASCII art? It’s likeBashscripting and not usingFIGlet;-)

Don’t Panic, its easy! 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-textual</artifactId>
<groupId>org.refcodes</groupId>
<version>3.4.1</version>
</dependency>
...
</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 AsciiArtBuilder().
withFontStyle( FontStyle.BOLD ).withFontFamily( FontFamily.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:

Here I changed the font and adjusted the AsciiColorPalette to be used by the algorithm:
AsciiArtBuilder theBuilder = new AsciiArtBuilder().
withFontStyle( FontStyle.BOLD_ITALIC ).withFontFamily( FontFamily.SANS_SERIF ).
withAsciiColorPalette( AsciiColorPalette.HALFTONE_GRAY ).
withColumnWidth( 120 );
System.out.println( theBuilder.toString( "HELLO WORLD!" ) );
What about inverting the output?

Here I adjusted the AsciiArtMode…
AsciiArtBuilder theBuilder = new AsciiArtBuilder().
withFontStyle( FontStyle.BOLD_ITALIC ).withFontFamily( FontFamily.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”:

The palette now consists of the three characters ., / and # representing the range from bright to dark:
AsciiArtBuilder theBuilder = new AsciiArtBuilder().
withFontStyle( FontStyle.BOLD_ITALIC ).withFontFamily( FontFamily.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:

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().create( LogoPixmap.FUNCODES );
RgbPixmap thePixmap = new RgbPixmapImageBuilderImpl().withImageInputStream( in ).toPixmap();
String theLine = new AsciiArtBuilder().
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 weak builder (also referred as builder in this context) 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
builderimplements one specific functionality, it is easily maintained and extended without resulting in hundreds of lines of complex code and dozens oftelescoping methods. Moreover, extending thebuilderwith new functionality does not break yourAPIas you would do by extending amethodof autility classwith 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
buildershould provide means to execute its main concern without changing thebuilderstate. 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
builderis that you easily find it with the type search of yourIDEas onebuilderimplements 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 patternis characterized by providing the functionality of autility class(or parts of it) being implemented with the means of thebuilder pattern, thereby moving configuration (and the like) concerns (otherwise passed to theutility class’s method as arguments) to the state of theutility-builderinstance while providing athread safebuilder method for constructing theutility-builder’s concern which only takes those argument(s) being passed which are directly related to the concern of theutility-builderclass and which must not be part of the state of theutility-builder’s instances.
Resources
The source codes of the refcodes-textual artifact is found at bitbucket.org.
Further reading
Continue with part q which discusses creating ASCII tables using a builder.
