• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Tim Cooke
  • Campbell Ritchie
  • Ron McLeod
  • Liutauras Vilda
  • Jeanne Boyarsky
Sheriffs:
  • Junilu Lacar
  • Rob Spoor
  • Paul Clapham
Saloon Keepers:
  • Tim Holloway
  • Tim Moores
  • Jesse Silverman
  • Stephan van Hulst
  • Carey Brown
Bartenders:
  • Al Hobbs
  • Piet Souris
  • Frits Walraven

When Does usage of the Builder Pattern Make Sense in Java?

 
Saloon Keeper
Posts: 1728
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Broken off from the other topic where it seemed (to me) the usage of builder was "cool, but not very readable/understandable or practical".

I used to try to explain away my lack of mastery of Design Patterns by saying "They are just workarounds for deficiencies in languages that we use.  I'd rather learn to use the facilities the languages I use provide me than learn a big catalog of generic ideas."  Pretty lame, but I realized the relative importance of some of them actually do vary from language to language.  Then looking at them more, that seems kind of true, even if I am now engaged in piling up a Big Catalog of Generic Patterns That Are At Least Sometimes Very Useful For Some Problems in Some Languages...

Let's look at the Builder pattern.  I was thinking how it would be pretty neat for actually making a pizza, at least at make-your-own places like Blaze, but anyway...

In Python or C#, named or optional/default parameters for your constructor can make this relatively unexciting, just pass the ones you think you want, if you get an invalid combination, you can just throw an exception.

Sure, in Java we have VarArgs if they are all of the same type, but default or optional parameters would make Builder a little less exciting, still a drag because they can only be trailing ones, we can't skip in the middle...but we don't have these in Java.

Named Parameters seemed horrible to me when I first saw them.
I felt they were exposing internal implementation to the outside world as part of the interface.
Well, they are, but they are convenient as heck.

In Java, with no named parameters and even neither default nor optional parameters, I guess we could make a pizza with a VarArgs of Toppings... but it feels like we would in general want recourse to the Builder pattern more often than if we had those Python-y options for our method parameters.

In this case, it seems a lot of people like to go for a Java implementation that is "StringBuilder-style", i.e. chainable with each method returning a mutated nascent object, well, with a call to say we are done chained at the end.

But some don't go anywhere near that, they look utterly different.
Many examples I saw looked like a solution in search of a problem, I could readily see a few different ways to approach things that seemed better, or even really forced just so they could show a Builder example.

I think some of the things that might make me want to use Builder in Java:
Some object like StringBuilder that really is normally "built up" from nothing to something over a series of steps -- this seems to be different than one consisting of different fields, but I like how StringBuilder works...

Or some Object type that has the following properties:
1. It is composed of several different fields, making it annoying or error-prone to try to specify them all together in one constructor, even with overloads.  Not having named parameters as an option, and also not having default parameters as an option, makes this more significant in Java than other languages I also use.  Not sure that having lots of optional data members on an object isn't often a sign of bad design already, but let's say we decided it was thought about and agreed to be justified as a design (or our boss insisted on it).

2. We would like for it to be immutable, in which case setters for the optional parts are a non-starter.  Tho there, we could return a new object with the additional parts added, so not sure that by itself is as big an excuse as I was first thinking.  If we are creating a lot of these objects, the return-a-new-object-with-stuff-added might be putting pressure on the garbage collector, or...

3. While there are several optional fields, not all of those combinations are logically valid.  The first thing that converted me from feeling "Encapsulation is annoying" to "Encapsulation is awesome!" was "This will make it impossible for some dweeb to get your object in an inconsistent state and then blame YOU for some crash or bad data."   Now just saying "use the setters" is even worse, because we don't want sloppy, confused or busy clients getting objects in some logically inconsistent state.  Again, it seems like maybe those things that need to be together should be unified into another class that gets passed in...maybe there are some options that are fine separately but are illegal combinations...I can see this making Builder attractive, I think.  The final step in many of the examples I see might be called .build() but is actually .verifyThatTheCurrentlyAssembledBunchOfParametersIsConsistentAndLegalThenEitherThrowAnExceptionOrBuild() so that means until we call it we might keep going from valid states to invalid states and back again, no harm, no foul until we decided we are done and called .build()

I don't know if I will be this reflective about "What is the proper usage of Design Pattern X in Java?" with other ones, but when I looked at some generally pretty competent sources, there seemed to be no consensus, also, many who often seem to provide good examples came up with ones that looked really unnatural/unnecessary/forced.

Thoughts?
 
Marshal
Posts: 5215
323
IntelliJ IDE Python Java Linux
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'd certainly recommend looking at a good example of a builder. My reference for such an example is Effective Java (any edition).

Let's consider why builders exist as a pattern. Immutability and Safety.

If you want to make a class immutable then its instantiation and setting of state must be done in a single operation. Of course this can be achieved using a constructor with arguments so why go to the bother of creating and using a builder? Constructors with many arguments are difficult to use and carry a high risk of being used incorrectly. Consider this constructor:

A contrived example, but as a client of this class all I have is this:

Um, now what?
This is where the builder helps because I now can do this:

Extrapolate this out to a class that has many members and consider how unpleasant the constructor would be to use. I tend to start considering a builder on my 4th constructor argument.
 
Sheriff
Posts: 16767
281
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tim Cooke wrote:Let's consider why builders exist as a pattern. Immutability and Safety.

... consider how unpleasant the constructor would be to use. I tend to start considering a builder on my 4th constructor argument.


There's also the matter of fluency or expressiveness, if you will.

Having a constructor or multiple constructors that take more than 3 or 4 parameters adds the smell of Long Parameter List and makes the code difficult to understand and therefore, a magnet to mistakes and errors, i.e., bugs.

The Builder allows you to provide a more fluent interface for class instantiation and configuration before an object's initial use.
 
Junilu Lacar
Sheriff
Posts: 16767
281
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tim Cooke wrote:
This is where the builder helps because I now can do this:


Good start in fluency. Here are some points to consider having a conversation about:

1. Is there a reasonable default for the "likes cows" idea? Would it make sense to assume a person either likes or dislikes cows and just provide a builder method to set it to the non-default value?

2. In the case of "likes cows", if we truly only have a binary state of like/dislike, then would it be better to just have likesCows() as the method and have another one for dislikesCows()? What this does is it avoids leaking the boolean value. Would it also make sense to have a third choice, isIndifferentAboutCows()? I'm not trying to stir up conversation about like/dislike/meh about cows specifically, I'm really thinking about the principle of not passing in a boolean as an argument.
 
Jesse Silverman
Saloon Keeper
Posts: 1728
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Interesting musing on the basics of data representation.

The classic thing that we see in ten million toy examples is age.

I hate that because when does that data ever update?

Keeping a birthdate around or even a year, makes sense because we can trivially compute their age at a given date for any time in the past or future.  But storing an age in a database?  Unless it is associated with a date (which it rarely is) it is literally meaningless.

The other things might be:

enum MarriageStatus { SINGLE, MARRIED, DIVORCED, WIDOWED }
MarriageStatus marriageStatus = MarriageStatus.SINGLE;

But Re-marriages happen, so you could be SINGLE (never-married), SINGLE (DIVORCED), SINGLE (WIDOWED)
or MARRIED (also DIVORCED) or MARRIED (also WIDOWED) etc.

I see things like this and worse in ten million examples that are trying to illustrate other things, but reinforce bad, simplistic ways of thinking about data.

Including these discussions in every example of say, how a constructor works is sub-optimal, every presentation would be overlong.

But avoiding them would be preferable to showing them and reinforcing bad modelling.
 
Rancher
Posts: 4062
56
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Interestingly, the original Gang of Four Builder pattern (1994) wasn't really about immutability at all.  It was about facilitating creation of complex objects by introducing a new class (or interface with multiple implementations) to handle that.  Typically a builder might return different specific implementations of a base type, depending on what parameters it's called with, and for complex object graphs it would be responsible for making sure all the parts were connected correctly.  Immutability wasn't really on most people's radar back then, it seems.

In the Java world, as late as 2006, Design Patterns in Java (Mesker/Wake) still has no mention of immutability in its description of Builder.  Which is funny, because they did mention Immutability prominently in the preceding chapter on the Flyweight pattern, and even gave the example of the String class as good example of Flyweight in Java.  Then, just a few pages later, they completely overlooked that StringBuffer / StringBuilder (the latter came out in 2004) was a good example of Builder in Java.  Perhaps because many people still weren't thinking of immutability as a main reason to use Builder.

It wasn't really until Josh Bloch came out with Effective Java, Second Edition (2008*), that the idea was prominently put forth to the Java world that Builder was good specifically because it facilitated making objects immutable (among other things).  Though we may infer from the name of StringBuilder in 2004 that some within Sun had this idea too... though it wasn't exactly prominent at the time.  And even Bloch didn't identify StringBuilder as a Builder - when he talks about Builder, he doesn't mention StringBuilder, and when he talks about StringBuilder under his section on immutability, he calls StringBuilder a "mutable companion class" to String.  

* Technically Bloch first published an article on Builder on the Sun site, which was a preview of the upcoming Effective Java Second Edition... I want to say it was in 2006 or 2007 originally.  There was a somewhat interminable wait for the book after that.  But the new version of Builder became known at the time as Bloch's Builder pattern, or the Effective Java Builder pattern.  Acknowledging that the focus was different from the original Builder pattern.
 
Tim Cooke
Marshal
Posts: 5215
323
IntelliJ IDE Python Java Linux
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Genuinely delighted to see that my contrived example of a builder has attracted some attention. This kind of discussion and reasoning is excellent and always results in better software and a better understanding of software.
 
Sheriff
Posts: 26943
83
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesse Silverman wrote:But storing an age in a database?  Unless it is associated with a date (which it rarely is) it is literally meaningless.



There are situations where "age" is what you want to store. For example if you're keeping track of historical data and your future use for that data is like "What fraction of people aged 25 to 29 reported harassment in the workplace?". In this case we really don't care how old the people are, only how old they were when the data was collected.

I ran into this situation early in my career; we had a contract with the government to review a primitive database where they had collected a list of all of their buildings along with a variety of data including their age. Not the year in which they were built, just their age. This database (which was probably a spreadsheet but I don't remember that) was already a fait accompli when we got involved and I wondered why they did it that way, but there was no point in bringing up the issue. It meant that the database wasn't designed to be updated in the future, but only to review the situation and decide what kind of hidden maintenance issues might exist. For example you might notice that many of your salt sheds were over 30 years old and might need replacing in the near future.

And the toy examples: for beginners it's easier to write code which collects an integer value. Otherwise, you can barely write an if-else structure and you're getting distracted by date arithmetic? Teachers would rightly avoid that swamp.
 
Jesse Silverman
Saloon Keeper
Posts: 1728
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mike Simmons wrote:Interestingly, the original Gang of Four Builder pattern (1994) wasn't really about immutability at all.  It was about facilitating creation of complex objects by introducing a new class (or interface with multiple implementations) to handle that.  Typically a builder might return different specific implementations of a base type, depending on what parameters it's called with, and for complex object graphs it would be responsible for making sure all the parts were connected correctly.  Immutability wasn't really on most people's radar back then, it seems.



This is why I come here so much!
So many presentations show what we would consider a Modern Java Builder pattern, then at the end show the original GOF (1994) page with its prominent definition that seems to not square at all with what they just showed, which left me wondering...WTF?

The original pattern still seems interesting, but is different enough from what I have been mostly seeing (which are generally the same idea in each case executed with varying degrees of daftness or deftness) that I'd prefer they had two different names.
 
Jesse Silverman
Saloon Keeper
Posts: 1728
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Junilu Lacar wrote:
Good start in fluency. Here are some points to consider having a conversation about:

1. Is there a reasonable default for the "likes cows" idea? Would it make sense to assume a person either likes or dislikes cows and just provide a builder method to set it to the non-default value?

2. In the case of "likes cows", if we truly only have a binary state of like/dislike, then would it be better to just have likesCows() as the method and have another one for dislikesCows()? What this does is it avoids leaking the boolean value. Would it also make sense to have a third choice, isIndifferentAboutCows()? I'm not trying to stir up conversation about like/dislike/meh about cows specifically, I'm really thinking about the principle of not passing in a boolean as an argument.



I don't think we should let this go, I see it in a LOT of examples (yeah, I am looking at lots of examples)...almost certainly all taken from the same book that someone read.  EDIT--I actually just saw this for the umpteenth time somewhere, which caused me to reply now before going back and agreeing to nod my head in agreement to get thru their presentation of what Builder means to them...

To some extent, this gets us the same problem that we have with NULL in SQL (and elsewhere).
Do we interpret NULL/no likesCows to mean we have no flipping idea how they feel about cows, or does it mean we no they don't like cows, it is in their signature on every post?

I am not sure if your dislike of a mandatory boolean for some such in a simpler constructor relates to this issue or another separate one.

So I see your point if it is that we could have this as one model of a data for a user:
boolean knownToLikeCows = false;
boolean knownToDislikeCows = false;

I am okay with even having a setter for this value on the class, people can change their minds.
Is it then considered to be an invalid state if someone were to set them both true?
Do we want to model the psychological state of strong ambiguous feelings in a user where depending on when and how one asks it will seem obvious that they love them or hate them?

I just went a little too far down the rabbit hole getting into psychology, climbing back out a bit, maybe having a boolean parameter begs the question of default/interpetation of default too much, and having two booleans leads us to the spectre of an inconsistent state (ambiguous feeling rabbit hole)...

should it be an Enum type like:

enum Sentiment { UNKNOWN, LIKES_MILDLY, LIKES, LOVES, LOVES_MADLY, DISLIKES_MILDLY, DISLIKES, HATES, COMPLETELY_DETESTS }

A simple pair of Optional boolean values for likesX, dislikesX solves the problem of distinguishing lack of knowledge from known to have no strong feelings, which could be important for some filtering or calculations, but leaves us with the problem of inconsistent state if someone tries to set both of them simultaneously (if that is illegal, it is one of the things that first made me like the Modern Java Builder pattern, allowing one to travel thru states that would be inconsistent or disallowed on their way to the final state combination that .build() sees)...

I personally do prefer being able to at least distinguish "no information" versus at least "default value which is potentially a real value and could be very wrong for this object/instance".
 
Marshal
Posts: 74606
335
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesse Silverman wrote:. . . same problem that we have with NULL in SQL (and elsewhere).
Do we interpret NULL/no likesCows to mean we have no flipping idea how they feel about cows . . .

Careful about changing languages. It is a bit like the German sympatisch, the Fernch sympathique, the Italian simpatico, and the English sympathetic. One of those words means something different from the other three. Yes, SQL can take NULL as an equivalent to, “don't know,” but Java┬« takes it as meaning no object. And you can't even apply null to a boolean variable, or other primitives.

enum Sentiment { UNKNOWN, LIKES_MILDLY, LIKES, LOVES, LOVES_MADLY, DISLIKES_MILDLY, DISLIKES, HATES, COMPLETELY_DETESTS } . . .

It's {TRULY, MADLY, DEEPLY}. For a moment I thought you had MILDLY twice, but I have now seen the underscores.
 
Jesse Silverman
Saloon Keeper
Posts: 1728
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It would be a Boolean, then.

And agreed, "false cognates/false friends" are a great hazard in many languages considered pair-wise or as groups.

I've seen the word terrific being read as a terrible insult by Italians, they equated it as "terrible" despite knowing a moderate amount of English.
 
Jesse Silverman
Saloon Keeper
Posts: 1728
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Junilu Lacar wrote:, I'm really thinking about the principle of not passing in a boolean as an argument.



Finally got to really look at that, went down a rabbit hole for a couple of hours, but it was worth it.

Part of my value is in the depth of my experience, but I have certainly worked with code that had so many code smells only an anosmic could deal with it...

https://en.wikipedia.org/wiki/Anosmia

While this wouldn't be the worst, second worst, or even third worst thing about code I'd become accustomed to dealing with, it is pretty bad.

I also have learned the AHA principle, which I heard of before, Avoid Hasty Abstractions.

That was from a branch off of a branch of following your link, but it's all good.
 
Jesse Silverman
Saloon Keeper
Posts: 1728
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Conditional explosion or over-proliferation as an anti-pattern is something that I had gotten to used to just "dealing with" in places I wasn't at liberty to fix it.  The boolean parameter thing is pretty closely related to this.  Boolean data members, less so, but still somewhat.

It is hard to measure these things, but I feel some of these little discussions will have long-term benefits in making code I get to design and write much better and more maintainable than code I have worked with and had limited liberty to improve without demonstrating "clearly erroneous behavior directly impacting customers".

Just because you are forced to deal with sub-optimal code in some situations (rather than fix it) doesn't mean it is okay for you to write new code committing the same sins...there is probably some maxim or aphorism describing this.

Many employers seem to want people who won't ever complain about these things in code they have to work with (they don't want to hire someone who is going to try to re-factor their whole codebase) yet won't continue to add to the problem by adding new code equally hard to read, change, reason about and work with.

Thanks again, Junilu!
 
reply
    Bookmark Topic Watch Topic
  • New Topic