4

Background

I have a class TextDrawable which draws text on top of a shape. There are a number of properties that can be set while creating a TextDrawable object.

Mandatory Properties:

  • Text
  • Shape

Optional Properties:

  • Color
  • Width
  • Height
  • Font
  • Border
  • ...
  • ... etc.

The class will have constructors that look like:

public TextDrawable(String text, Shape shape) {
}

public TextDrawable(String text, Shape shape, int color) {
}

public TextDrawable(String text, Shape shape, int color, int width, int height) {
}

// and so on..

Builder Pattern :

I decided to use the Builder Pattern to make it more convenient to create TextDrawable objects. I followed the design as explained in Effective Java, and here in this highly upvoted answer. I came up with the builder class which will be used as follows:

TextDrawable drawable = new TextDrawable.Builder("AK", Shape.RECT)
        .width(10)
        .height(10)
        .useFont(Typeface.DEFAULT_BOLD)
        .build();

Possible Problem:

There seems to be a problem with this design. Since the "mandatory" fields are passed in the constructor of the builder, the builder cannot be reused.

What if I wanted to create another TextDrawable of same height, width, font etc but with different text and color. I will have to create another builder object and repeat the same code as above.

TextDrawable oct = new TextDrawable.Builder("OCT", Shape.RECT)
        .width(10)
        .height(10)
        .useFont(Typeface.DEFAULT_BOLD)
        .build();

TextDrawable nov = new TextDrawable.Builder("NOV", Shape.OVAL)
        .width(10)
        .height(10)
        .useFont(Typeface.DEFAULT_BOLD)
        .build();

Proposed Method:

What if I move the mandatory parameters from the constructor of the builder to the build method as follows:

TextDrawable drawable = new TextDrawable.Builder()
        .width(10)
        .height(10)
        .useFont(Typeface.DEFAULT_BOLD)
        .build("AK", Shape.RECT);

This way allows for more flexibility as I can create one instance of the builder and reuse it to create multiple objects. (Also avoids repeating the same code every time I create a new object.) The new usage will look something like:

TextDrawable.Builder builder = new TextDrawable.Builder()
        .width(10)
        .height(10)
        .useFont(Typeface.DEFAULT_BOLD);

TextDrawable oct = builder.build("OCT", Shape.RECT);
TextDrawable nov = builder.build("NOV", Shape.OVAL);

It prevents boilerplate code. Also allows the build method to fit into the fluency idiom as follows:

TextDrawable oct = builder.buildRect("OCT");
TextDrawable nov = builder.buildOval("NOV");

Questions:

  1. Is the problem identified above valid or do I lack understanding of the builder pattern? Should we not reuse builder objects to created multiple instances?

  2. Is the proposed modification to the well known builder pattern valid? Are there any disadvantages of moving the "mandatory fields" into the build() method?

  3. If there are some possible drawbacks, what is a better way of achieving what I want? Should I be using a director object or something that encapsulates the builder to achieve what I want?

  • 1
    One possible drawback to re-using the builder in this way is that if an optional attribute isn't specified while building a later object, it will inherit the value(s) specified while building earlier objects. – Mike Partridge Oct 15 '14 at 15:15
2

1. Is the problem identified above valid or do I lack understanding of the builder pattern? Should we not reuse builder objects to created multiple instances?

It can be, depending on your situation. Here you've identified that you have mandatory fields that are unique, therefore making them parameters to your build method allows you to reuse the same pre-configured builder over and over, which is handy.

2. Is the proposed modification to the well known builder pattern valid? Are there any disadvantages of moving the "mandatory fields" into the build() method?

As far as advantages or disadvantages go, I think that the other option you haven't considered here is allowing the required fields to be set the same way the other fields are. Take the following code for example:

TextDrawable.Builder builder = new TextDrawable.Builder("OCT", Shape.RECT)
        .width(10)
        .height(10)
        .useFont(Typeface.DEFAULT_BOLD);

// Make the rectangle
TextDrawable oct = builder.build();

// Build the new shape
TextDrawable oct = builder.name("NOV").shape(Shape.OVAL).build();

Obviously this is very similar to what you had though, and either approach is equally valid. It really comes down to how much you want to configure, and how much you know will always change. If you'll be building a bunch of rectangles of the same size, but only changing the name, this solution allows you to remove the shape parameter.

3. If there are some possible drawbacks, what is a better way of achieving what I want? Should I be using a director object or something that encapsulates the builder to achieve what I want?

A director would allow you to set up predefined builders more easily. You could have a director for making circles of the same size, or a director that takes in only one length and only makes squares. It's job is to reduce the boilerplate even further, but does so at added class complexity of the overall application. I would really only go this option if you are going to absolutely need it, and stick with your solution of re-using builders where possible.

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.