The Builder Pattern in DrJava

I’m currently in the process of dealing with a confusing error message that one of our users got when he tried to save his files in DrJava. The problem is actually pretty simple: The files were read-only, and he got a “Permission denied” message that, and I agree, was too broad to nail down the exact nature of the problem.

What I’m planning to do now is display a better error message, and ask the user if he wants to overwrite the files even though they are read-only. Generally, this transfers to failed attempts to save. Here, I want to present the user with a list of files that could not be saved and ask the user if he wants to retry saving them. For read-only files, there is a clear solution available: make the files writable (even though Sun, in their inexplicable enterprise to keep Java development challenging and interesting has provided the File.setReadOnly() method, but no method to make a file writable). For writes that failed for other reasons, there is no clear panacea, but a “Retry” option seems useful.

There already was a dialog that does much of what I need, ScrollableListDialog. It has been used to display a list of files that could not be found and have therefore been removed from the project. Now I’m going to extend that dialog so it supports more than just one button (“OK”), lets the programmer know which button was pressed, and let the user select files in the list. So it’s getting a bit more complicated.

When I looked at the constructor, I decided that it was already way too complicated. It was badly designed and as a result error-prone and time-consuming to deal with. Here is the main constructor of the class:

public ScrollableListDialog(Frame owner,
String dialogTitle,
String leaderText,
Collection listItems,
int messageType,
int width,
int height,
Icon icon,
boolean fitToScreen) { /* ... */ }

There were additional constructors that supplied default values so that not all values had to be set manually. Still, extending this structure even further meant adding even more constructors. I decided this was the wrong path. Instead, I chose the Builder design pattern.

The constructor for ScrollableListDialog is private, so instances cannot be created directly. Instead, there is a static class ScrollableListDialog.Builder that starts out with the default values in its fields. Builder has setters to change these values, and a factory method ScrollableListDialog build() that creates the ScrollableListDialog so configured. All of the setters return this, so setting different values can be chained together. Consider this explicit constructor call (using one of the other constructors that I haven’t shown):

ScrollableListDialog dialog = new ScrollableListDialog(MainFrame.this,
"Files Not Found",
"The following files could not be found and have been removed from the project.",
filePaths,
JOptionPane.ERROR_MESSAGE);

This isn’t even the full-blown constructor shown above, but it already isn’t clear which of the strings is the title and which the text for the dialog body. If we add width and height, it isn’t clear which comes first. Type systems do not help here. Using the Builder pattern, the above becomes a bit more verbose, but much clearer:

ScrollableListDialog dialog = new ScrollableListDialog.Builder()
.setOwner(MainFrame.this)
.setTitle("Files Not Found")
.setText("The following files could not be found and have been removed from the project.")
.setItems(filePaths)
.setMessageType(JOptionPane.ERROR_MESSAGE)
.build();

Not only is this type-safe, but it also pairs up the value being set with its name. Using the Builder pattern, Java can achieve something similar to named variables, where the name of the variable has to be stated when passing an argument. The order in which the values are set is arbitrary, and only those values need to be bothered with that are actually important in the specific situation.

I should mention two little issues: First, some people may ask why everything in the Builder class can’t be static. Then I would not have to new the builder class and create another object. In single-threaded code, that would be fine, but if the Builder class is accessed from two different threads simultaneously, their settings would interfere with each other. Second, the builder pattern makes it slightly harder to subclass ScrollableListDialog: In addition to subclassing that class, the Builder class has to be subclassed as well so that it actually builds an instance of the new subclass.

Now that the Builder is in place, I can easily extend ScrollableListDialog without having to worry that other users do not know what to pass in for the canSelect flag, for example.

Share

About Mathias

Software development engineer. Principal developer of DrJava. Recent Ph.D. graduate from the Department of Computer Science at Rice University.
This entry was posted in Code Pranger, DrJava. Bookmark the permalink.

Leave a Reply