0
\$\begingroup\$

Despite being the clever Java coder I am, I noticed I know way too little about various design patterns. The builder pattern caught my eye as something to learn, as I have certainly seen my fair share of telescopic constructors already.

So, I decided to give it a try. The following is a really simple class represents a cell in a table of data. So far a cell can have text (its contents) and a URL (if it is to be rendered as a hyperlink), but I could see myself adding other parameters as well, so there's an inner static class called CellBuilder as well.

import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A class that represents data in a table cell.
 */
public class CellData {
    /** The text that's displayed in the cell. **/
    private final String text;

    /** The URL to which the text should link to, if any. **/
    private final URL url;

    private CellData(CellBuilder builder) {
        this.text = builder.text;
        this.url = builder.url;
    }

    public String getText() {
        return text;
    }

    public URL getUrl() {
        return url;
    }

    /** A builder to avoid telescopic constructors. **/
    public static class CellBuilder {
        private String text = "";
        private URL url = null;

        public CellBuilder text(String text) {
            this.text = text;
            return this;
        }

        public CellBuilder url(String url) {
            try {
                this.url = new URL(url);
            } catch (MalformedURLException ex) {
                Logger.getLogger(CellData.class.getName()).log(Level.SEVERE, null, ex);
            }
            return this;
        }

        public CellData build() {
            return new CellData(this);
        }
    }
}

A new cell would then be created, for example, like this:

CellData cell = new CellData.CellBuilder().text("hello").url("http://www.example.com/foo").build();

Now, I don't know about you, but to me that doesn't look much more easier or cleaner than doing it the traditional way, by passing those arguments to the cell in its constructor. :( Yet another way would be to do it like this:

CellData cell = new CellData();
cell.setText("hello");
cell.setUrl("http://www.example.com/foo");

Why shouldn't I do it like that either? I wonder if this particular use case is still too simple to get the best out of this design pattern, or if I'm doing something else completely wrong?

Notice that this doesn't strictly conform to the architecture presented in Wikipedia, but that's because I read about this way from some other source where it was more like this, and the Wikipedia way with about five classes felt like too complicated for a simple matter like this.

\$\endgroup\$
  • 1
    \$\begingroup\$ Guava uses the builder pattern frequently in their helper/utility classes. These classes are intended to be highly customizable to a user's specific situation while remaining fluent/easy to use. A concrete object with less than 5 parameters is probably too simple to bother with a Builder. Examples: code.google.com/p/guava-libraries/wiki/… \$\endgroup\$ – Eric P. Nov 29 '12 at 17:59
1
\$\begingroup\$

Builders are often employed when complex structures or records with lots of attributes need to be set. But more simply they are used to keep an object immutable. A prime example of that is the StringBuilder class which is used to build immutable Strings: the object isn’t very complex – in fact, in terms of data it’s as easy as it gets – but the logic to create them might be.

The StringBuilder class is needed because strings themselves are immutable for performance (and other) reasons. As a consequence, lots of operations on strings create new strings. If many such operations are chained, this becomes inefficient. The StringBuilder class mitigates that by providing a mutable string which can be modified repeatedly, and only then transformed into an immutable string.

Another example from one of my projects was a complex class which represented a special class of graphs. Again, for performance reasons and to keep its API simple, this class was designed to be immutable. So in order to to create and mutate graphs I provided a builder class which exposed methods for modifying its internal graph structure, and a conversion method to an immutable graph.

Immutability is a highly recommended feature of classes. This is why you shouldn’t choose the second method you proposed, i.e. have a class with lots of setters. In fact, setters are often code smell.

\$\endgroup\$
  • \$\begingroup\$ Hey, thanks for the interesting links! So far this seems like the best answer. I didn't even think of the pattern being useful with regards to immutability, but now that you mentioned it with that link, it actually makes a lot of sense. Although I'm still going to have to meditate on the recommendation of making most classes immutable. :) \$\endgroup\$ – ZeroOne Nov 30 '12 at 21:21
  • \$\begingroup\$ It's too bad that the String/StringBuilder [or StringBuffer] pattern used for storing collections of characters wasn't carried through with other types of collection. Even if converting an immutable collection to a mutable one, modifying it, and converting it back to an immutable one would require a full data copy for each direction of conversion, it would eliminate the need for many other defensive-copy operations. \$\endgroup\$ – supercat Jan 24 '14 at 17:10
  • \$\begingroup\$ @supercat What’s wrong with Collections.unmodifiable* then? Back when I used Java I used that obsessively. Granted, that’s “only” interface immutability but as far as I’m concerned it’s enough. It’s only one step more complicated to mutate a string. \$\endgroup\$ – Konrad Rudolph Jan 24 '14 at 21:22
  • \$\begingroup\$ @KonradRudolph: If code receives a List<String> and wishes to take a snapshot of its contents, is there any way by which it can know whether it needs to create a new object holding those items, or whether storing a reference to the received object would be sufficient? \$\endgroup\$ – supercat Jan 24 '14 at 21:46
  • \$\begingroup\$ Ah well, no, not that I know of. \$\endgroup\$ – Konrad Rudolph Jan 24 '14 at 22:56
4
\$\begingroup\$

Your building pattern is just right. But the situation (as elsewehere mentioned) to use the building pattern is not. Imagine a situation where there is a (in your case) celldata base class:

 class CellData {
 }

This class is the base of other, more specific, celldata class:

 class CellData_2  extends CellData {
 }

etc.

Now imagine the choice of which specific celldata you are requiring, depending on the given URL your builder method starts to look something like this:

 public CellData build (String URL) {
     if (URL complies to case 1) {
          return new CellData_1;
     ...
 }

That's where the builder pattern should be used.

\$\endgroup\$
  • 2
    \$\begingroup\$ I think you’re confusing the builder pattern with the factory method pattern. While dynamic constructor dispatch can be done by the builder, that’s not its main purpose. The main purpose is exactly what OP has said (but his use-case is rather restricted). Consider Java’s StringBuilder class. No inheritance or dynamic dispatch is used here. \$\endgroup\$ – Konrad Rudolph Nov 29 '12 at 21:43
  • \$\begingroup\$ Thanks for you answer, @dmaij. :) It would make sense to me to add some factory-like features to the builder like this. \$\endgroup\$ – ZeroOne Nov 30 '12 at 21:23
0
\$\begingroup\$

The builder pattern really shines when you have complex logic around what you are trying to construct. I use this pattern frequently in my unit test projects. If I need to setup some dependencies that require mocking, but the interactions aren't central to my test, then I don't want to see it in my test. A builder is also useful for setting defaults that are required by the object under test, but again, not central to what I am trying to test. Putting all this code in a builder gets it out of the way. It also keeps the code DRY (Don't Repeat Yourself), so that if there is a change to how that object is instantiated, there is only one place to fix all my broken tests.

If you don't have this kind of complexity in your object, then I wouldn't bother with the pattern.

If you are looking for a way to avoid a long constructor parameter list, I would suggest waiting until it gets long, and then evaluating what your class is doing. Perhaps some parameters are only needed during the execution of some method. If that's the case, just pass those parameters in when you call the method. If you feel you can't do that, then maybe the class is starting to violate the Single Responsibility Principle. Look to see where those parameters are used, and maybe you can identify which ones go together, and split out another class.

It's a useful pattern, but I think your initial assessment is right: that this use case is too simple for it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.