10

Recently I shared with my team the Builder Pattern(fluent style) for unit testing.

I personally like to use that pattern for unit testing, since many objects are usually created and with different parameter configurations. The pattern helps keeping the unit tests short, specially if combined with Object Mother pattern.

Now some members of the team want to use it in other areas of the project, and it looks to me like it may spread everywhere...

What are the implications?

I understand that there is a cost on creating the Builders, but since we already have them, why not use them?

5
  • 7
    Why do you think it would be bad? What prompted you to ask this question, rather than agreeing with your colleagues that it would be useful in other parts of the codebase? Commented Nov 27, 2020 at 16:40
  • 1
    I don't disagree with them, I asked why would it be bad, and gave one reason why it would(the cost of creating Builders) and I also asked Why not use them since we already have them...), but whenever I study about design patterns I always hear "don't overuse them" "don't start your code with patterns", etc. So my problem is that just because I can not come up with reasons not to use it everywhere it doesn't mean that there is no reasons...which is the reason I'm asking here Commented Nov 27, 2020 at 17:54
  • The reason I asked is that it's a rather vague question: asking if there are any situations where it would be bad to use the Builder Pattern leads to a trivial "yes"; asking to list all of them leads to an open-ended list. A more useful question would describe the way you're planning to use it, and then you could ask if that was applying it correctly, or if there were dangers you hadn't spotted in your specific situation. Commented Nov 27, 2020 at 18:01
  • @IMSoP I can not entirely disagree with your opinion, maybe the community will decide to close the question for the reasons mentioned by you. But it's not uncommon to have examples on when not to use a pattern. I gave one. Commented Nov 27, 2020 at 18:11
  • 2
    @Juan: I think a more neutral wording could help to give the question a better reception and took the freedom to change it a little bit (you can undo my changes if you don't like them). Commented Nov 27, 2020 at 19:26

4 Answers 4

23

Everyone wants a silver bullet that will let them turn off their brain. This isn’t it either.

Object construction, and its associated creational patterns, is a many and varied thing.

I presume you’re referring to Joshua Bloch’s builder pattern. This is fine when you want an immutable object and have a need to simulate named arguments because your language doesn’t have them.

But mastering Bloch’s builder is no excuse to ignore all the other creational patterns and techniques. Factory method, abstract factory, the GoF builder, hell even the much maligned singleton pattern, all have situations where they should be considered.

And, believe it or not, Bloch’s Builder can even be outclassed in the realm of fluent construction. Study the eDSL pattern of construction and you’ll be able to enforce construction rules at compile time. Which is nice but takes a fair bit of work to set up and is not always needed.

You may like your shiny new hammer, but please don’t pretend everything is a nail.

4
  • I used the builder pattern in a similar style as can be seen in the accepted answer in stackoverflow.com/questions/59021513/… Commented Nov 27, 2020 at 18:28
  • 2
    "all have situations where they should be considered." Could you list a couple of examples to drive the point home? Commented Nov 28, 2020 at 9:56
  • 3
    The singleton pattern can be used when an immutable sentinel object is needed in an object-oriented language, or you otherwise need a pointer to a known “not real data” value other than NULL. Commented Nov 28, 2020 at 12:14
  • 1
    I think the problem is less "wanting a silver bullet" and more in too many rules of the form "don't do this and that" when you have a situation before you where doing "that" seems like a good, easy idea. Too many things stated dogmatically. Commented Dec 20, 2020 at 19:34
11

A pattern is only bad if it's not used properly or it's not fitting for the specific use case. Builder pattern is helpful to encourage immutability by using final modifiers on all the instance variables without having a super long constructor where everything has to be set, however, for objects with only a small number of heterogeneous fields it may be an overkill.

Also Mandatory + optional member fields is a feature of the Builder pattern described in Effective Java, however, it could also mean there is no way to tell at compile time if all necessary fields are set (you get NPE at runtime) --- so all necessary fields must be provided with default values to avoid this happens.

5
  • 1
    Since we already have the Builders, Why not use them? any disadvantage? Commented Nov 27, 2020 at 17:56
  • 5
    like I said about the pros/cons in the answer, it's not a yes/no option but more like how is it used in your case -- e.g, if there are redundant code compared with normal constructor(s), if someone forgets to include a filed in builder, how easy could the error being caught, etc Commented Nov 27, 2020 at 18:09
  • 1
    @Juan: Since you already have a bicycle, why not use the bicycle when you need to go to the kitchen? Commented Nov 30, 2020 at 14:20
  • @Flater Good point, though the world is full of people who takes the car to go to the gym and sit on a static bike... Commented Nov 30, 2020 at 14:45
  • @J.J the reason for that is because other people drive cars and a Bicycle helmet can only do so much. Commented Apr 1, 2021 at 22:22
4

You wrote

but since we already have them, why not use them?

so I guess you are talking exclusively about cases where you already implemented a Builder for unit testing, and now consider to reuse it outside of unit tests? That's probably fine, as long as you did not made any compromises regarding code quality because of some "it's just code supporting tests so it does not have to be clean"-mindset. The unit tests using the Builders then become automatically tests not just for the main "Subjects Under Test", but also for those Builders, so there is indeed no extra cost here.

Let me add that your literal question reads quite differently: the phrase "using a Builder everywhere" gives me the impression of someone introducing Builders for each and every class, even it they have zero or just one constructor parameter. That would be pretty useless overhead for no apparent benefit, and I think it is pretty self-evident why this does not make much sense.

0

Should you use it everywhere? Let's list some pros and cons.

Pros

  • Explicitly configure an object with natural language-like methods using chaining.
  • Intermediate builder types can enforce that some properties only/not set after other properties. For example a REST request builder may have intermediate types after the Verb. Some intermediate verb builder would contain methods for setting body (POST/PUT/etc), and some would not (GET/etc).
  • Builders can run validation that exists outside of the object being created.
  • Clear delineation between configuration/mutability and building/immutability. Object configure and object produced are clearly different.
  • In typed languages, IDEs can make using builders very pleasant.

Cons

  • Extra boilerplate. Most languages don't have a built in syntax for builders. IDEs can generate builders for you, but this isn't always available.
  • Extra knowledge required for future developers. Developers must now know to create an object via a builder instead of standard initiation. Also they may be confused by builder internals, or frustrated the builder does not allow their desired configuration (this could also be a pro under validation).
  • Extra code overhead to create an object. For example, now you need to import and initialize the class and its builder, not just the class. Usually this code is eclipsed by the builder methods, but if few builder methods are called, it's just extra work.
  • More code to maintain. New properties need to be added to the original class and the builder. Also the builder itself may have bugs.

Builders have an upfront cost that is paid down each time someone uses it create an object. This is most often advantageous when shipped as part of a library, so other developers can easily configure objects to use with the library. You are going to be hard pressed to pay that off for internal code. Most of the time an object is only create in a few spots in internal code. The one exception you found is with tests, where an object may be created in hundreds of different configurations.

Like all software design choices, it's a judgement call based on weighing options. I view the value of a builder as proportional to P x I, where P is the number of parameters used to configure the object, and I is the number of places its initialized. If either of those values is low, its probably not worth it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.