2

I know there are several questions about the builder pattern.
- Use builder pattern from the constructor in a subclass
- When would you use the builder pattern
- Java improving builder pattern on a specific class
- Builder design pattern why do we need a director

So far I used the builder pattern like descripted in Bloch Item 2.

Yesterday I changed some small detail. I added a public constructor for the default values. So you could use both the Builder for a complex object or a constructor for simple object with the necessary values.

public class Blub {
  private final String id;
  public Blub( final String id ) {
    this( Blub.Builder(id) );
  }

  private Blub( Builder builder ) {
    this.id = builder.id; 
  }

  public static class Builder {
    private final String id;
    public Builder( final String id ) {
      this.id = id;
    }

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

But I am not sure if this has a design flaw. Because if I finalize the class I'm sure there are no disadvantages. So in the easy case, it would be possible to do call the Blub constructor.

new Blub("1a");

instead of

(new Blub.Builder("1a")).build();

But if I don't finalize the class it would be possible to extend it and do all kind of stuff in the new constructor. And I am not sure if there are no cases to mess this up now. Any idea?

Community
  • 1
  • 1
Christian
  • 1,664
  • 1
  • 23
  • 43
  • If someone extends your class, it's their job to make sure they do so correctly. I don't think I'd worry about what happens if they do so poorly. You can't fix stupid. For that matter they could override every method of every class to do the exact opposite of what it should do- you can't stop that either. So don't worry about it. – Gabe Sechan Aug 24 '13 at 09:50
  • 1
    @GabeSechan Wow, I can't disagree more. Classes should be designed for inheritance or it should be forbidden. You're creating future bugs in the software. – Eric Stein Aug 24 '13 at 11:56
  • @Christian what is the issue with making Blub final? – Eric Stein Aug 24 '13 at 11:57
  • @Eric Blub represents an electrical assembly, so maybe one day somebody wanna concrete it in more detail .. so I just don't wanna forbid it. – Christian Aug 24 '13 at 12:07
  • 2
    @Christian Sorry, it's early. You can't override a constructor. Just don't let the constructor call any methods which can be overridden. You should also consider putting an interface in front of Blub, making it final, and letting future clients use composition if they need Blub's functionality. – Eric Stein Aug 24 '13 at 12:10
  • @Eric yeah your right. I will change this in the question. But If somebody creates an own constructor, they have to call this one. And If there is nothing else to ensure. I am fine. – Christian Aug 24 '13 at 12:14
  • One more link I found in another thread. About subclassing with a builder. https://weblogs.java.net/blog/emcmanus/archive/2010/10/25/using-builder-pattern-subclasses – Christian Aug 24 '13 at 12:18
  • @EricStein I can't disagree with you more. If you're writing a library, you have to assume that the people who use it know what they're doing. Preventing them from deriving from a class is pointless (they can just recompile the jar after stripping the word final) and needlessly frustrating. WHich is why most languages don't even have final classes- they're an annoyance and a bug, not a feature. – Gabe Sechan Aug 24 '13 at 18:05
  • @GabeSechan Well, Josh Bloch and Jaroslav Tulach are the biggest names I know in Java API design. They both agree that designing for extension or forbidding it is the way to go. I am not going to explain the reasons in a comment. I'm pretty sure that most OO languages do have an equivalent. Perhaps you could indicate one that doesn't? – Eric Stein Aug 24 '13 at 19:48
  • @EricStein Appeal to authority is a logical fallacy. Quote their logic, not their names. Although I can't see any reason for forbidding something just because you don't expect someone to want to do it- not unless you have a good specific reason for that. My guess is their logic is "it doesn't fit my vision" which is always a poor one. As for languages without it- C++ doesn't. If Python does I don't know about it. Ruby doesn't appear to, although I don't know it well enough to be sure. OO Perl doesn't. Java is actually the only one I can think of off the top of my head that does. – Gabe Sechan Aug 24 '13 at 19:55
  • @GabeSechan It was more an appeal to 500 characters. As I said, this is not the appropriate venue for explaining the design for inheritance philosophy. C++ has final since C++11. C# has always had it. It appears that the scripting languages do not, which only somewhat surprises me. – Eric Stein Aug 25 '13 at 00:07
  • @EricStein Yeah, C++11 added a lot of bad ideas from Java. This would be one of them, although at least its a mostly harmless one- easily overridden by a quick change to the header file. Here's the end question- if someone wants to derive from your class and is willing to accept the risks/work that entails, why should you care? I've never seen a case where a final class gave me any advantages, and once or twice I've seen it force me to put in a work intensive facade to work around it. – Gabe Sechan Aug 25 '13 at 00:15
  • @GabeSechan For starters, it commits you to an implementation. Once somebody extends your class, they're relying on its internals. You can't take that back. You have to worry about the Liskov Substitution Principle. Subclasses of concrete classes can't correctly implement equals(), which makes it harder/impossible to correctly implement hashCode(). You get unhappy users because you let them shoot themselves in the foot. Documenting how to safely and correctly use their sublclass in your library is a pain, unless you're of the school that says they need to read your source to figure it out. – Eric Stein Aug 26 '13 at 01:32
  • @GabeSechan They're forced to accept any new methods that get added to the parent class, whether or not they're applicable to the subclass. – Eric Stein Aug 26 '13 at 01:35
  • @EricStein Yes, relying on the internals of a class is the entire point of inheritance- code reuse. Not to mention many times you can extend it in interesting ways without requiring knowledge of the internals. And subclasses CAN correctly implement equals- call the superclasses equals function and use the result of that. Its done every day. Don't get me wrong- I agree with documenting how to do so correctly. I just see 0 value in preventing them if they want to take on that risk. That's their decision, and they may have valid reasons you can't understand when you write your class. – Gabe Sechan Aug 26 '13 at 15:07
  • @GabeSechan I did more reading. It is technically possible to correctly implement equals(), as explained here: http://www.artima.com/lejava/articles/equality.html. It requires a third method that you need to hope the extending class remembers to override. You're also ignoring all the problems that happen when people pass back into your library code that breaks the fundamental assumptions you make about your classes because that's how you wrote them. Or is that just their problem, and if they screw it up it's their fault? That's not a good attitude if you want a high adoption rate. – Eric Stein Aug 26 '13 at 15:53
  • @GabeSechan Here's an example. I do interviews all the time. I ask developers to write Square and Rectangle classes. Virtually 100% of the interviewees have Square extend Rectangle. Then it takes me a half an hour to help them realize that you can't actually do that reasonably. If people can't handle that trivial case without somebody pointing out flaw after flaw, what makes you think they're going to do it right with a hard case? More likely, they'll make something that looks like it works, and does 80%-95% of the time, and fails the remainder for non-obvious reasons. – Eric Stein Aug 26 '13 at 15:58
  • @EricStein That's because over 60% of candidates fail FizzBuzz. It has nothing to do with extending classes. And I 100% disagree with the idea of restricting what a decent programmer can do because other programmers are stupid. Not to mention, your evidence is contrary to decades of my experience of me and all my coworkers deriving from classes just fine. Sorry, but I think your philosophy is wrong, and only causes trouble. You aren't going to convince me otherwise. – Gabe Sechan Aug 26 '13 at 17:36
  • @EricStein Also, the square/rectangle thing is a HORRIBLE example because there ISNT a clear way to derive from it. The nature of the relationship changes depending on what you want to do. That's why the whole Shape example is something you shouldn't do anymore- it has no relationship to real programming. – Gabe Sechan Aug 26 '13 at 17:38

2 Answers2

2

If you're going to use a Builder (or Factory pattern for that matter), it is usually counter-intuitive to have parameterized constructors as well (unless they are dependencies and you are using IoC). You are essentially breaking the orthogonality and DRY principle. Builder and Factory patterns are usually used to solve construction challenges where creating an instance is non-trivial or otherwise error prone.

In your case, this does not seem to be.

Is it a design flaw? No. Can it potentially cause the API to become confusing? Absolutely. Particularly over time, as more code divergers and starts using either method A and B. Are you going to explain to new developers when they should use method A for creating objects or when they should use method B?

Mikkel Løkke
  • 3,710
  • 23
  • 37
1

One thing that I often do is provide a builder AND static factory methods to build the common simple cases. This way you can still have a private constructor and get intent revealing clean ways to construct the default cases.

Blub blub = Blub.createWithId("id");

This also lets you add several static factory methods without having to have multiple constructors or constructors with confusing parameters.

dkatzel
  • 31,188
  • 3
  • 63
  • 67