Very often one has to create a number of of similar objects. Using JavaFX builders for this task instead of constructors has several advantages as we will see in this article.
In short, builders…
- are easier to read and understand
- avoid some “copy & paste”-bugs
- result in small, but simple code, if builders are reused
When using JavaFX, you typically have to create a number of similar objects with only few attributes different. For example your screen may contain buttons, which have the same size and style, but different locations, labels, and event handlers. Or you want to use similar transitions for some elements.
The traditional approach is to create all objects with constructors and configure them with setters. Listing 1 shows an example of three similar labels with the same font and color, but different locations and content.
final Text text1 = new Text(50, 50, "Hello World!"); text1.setFill(Color.WHITE); text1.setFont(MY_DEFAULT_FONT); final Text text2 = new Text(50, 100, "Goodbye World!"); text2.setFill(Color.WHITE); text2.setFont(MY_DEFAULT_FONT); final Text text3 = new Text(50, 150, "JavaFX is fun!"); text3.setFill(Color.WHITE); text3.setFont(MY_DEFAULT_FONT);
Listing 1: Creating similar objects with constructors
What an awful piece of code! We could put everything in a loop, which would make the code shorter, but definitely not simpler. Another option is to use builders straight-forward, one builder per created object. Listing 2 shows the code accomplishing the same task.
final Text text1 = TextBuilder.create() .text("Hello World!") .x(50).y(50) .fill(Color.WHITE) .font(MY_DEFAULT_FONT) .build(); final Text text2 = TextBuilder.create() .text("Goodbye World!") .x(50).y(100) .fill(Color.WHITE) .font(MY_DEFAULT_FONT) .build(); final Text text3 = TextBuilder.create() .text("JavaFX is fun!") .x(50).y(150) .fill(Color.WHITE) .font(MY_DEFAULT_FONT) .build();
Listing 2: Creating similar objects with one builder per object
Obviously the code became longer, but in my opinion even this version is preferable for a number of reasons.
Builders are easier to read and understand
JavaFX Builders make it obvious which parameter sets which property. In the case of Text, we can guess, that probably the doubles set the location and the String sets the content. But often it is not that obvious. Take a look at this Rectangle creation for example:
final Rectangle rect = new Rectangle(100, 100, 50, 50);
Is (100, 100) the position or the size? Now compare that to the version using a builder. It is longer, but also a lot easier to understand:
final Rectangle rect = RectangleBuilder.create() .x(100).y(100) .width(50).height(50) .build();
Builders avoid a number of possible “copy & paste”-bugs
Usually one creates code blocks like these in Listing 1 and 2 by typing the first part and using “copy & paste” for the rest. And almost always, when I do this, something goes wrong because I overlook a part that needs to be changed in the copied sections. I guess, I am not the only one.
When you take a closer look at Listing 1 and 2, you will notice, that Listing 2 requires less changes in between the different code blocks. In Listing 1, the variable names text1, text2, and text3 are used several times, thus they also need to be changed several times. In Listing 2 the variable names are used only once.
Builders result in small, but still simple code, if reused
A not very well known and often overlooked feature of the JavaFX builders is, that they can be reused. Not only can they be reused to create similar objects, but also to create slightly different objects. Listing 3 shows how the three Text objects can be created with a reused builder.
final TextBuilder builder = TextBuilder.create() .x(50) .fill(Color.WHITE) .font(MY_DEFAULT_FONT); final Text text1 = builder.text("Hello World!").y(50).build(); final Text text2 = builder.text("Goodbye World!").y(100).build(); final Text text3 = builder.text("JavaFX is fun!").y(150).build();
Listing 3: Creating similar objects with a reused builder
Instead of calling the build() method immediately on the builder to get the created object, we store a reference to the builder and reuse it. The properties, which are common to all objects (x, fill, and font) have to be specified only once. Then we take the builder and set the properties, which are specific for a single object (content and y) and call build(). This will create an object with the properties as they are currently specified in the builder.
In my opinion, this is the best solution to create the three Text objects. It avoids code duplication; all common properties are only specified once. At the same time it is explicit, specifying exactly what is going on. And as a little bonus, “copy & paste”-bugs are almost completely avoided, because all properties in the copied sections have to be altered and are not as easily overlooked.
Next time you have to create a number of similar JavaFX objects, I encourage you to try the pattern above.