Wednesday, November 7, 2012

java - Builder (Joshua Bloch-style) for concrete implementation of ...

I don't think it's a good idea to think of builders that way. Generally, when you need to use a builder to create an instance of a concrete class you already know which class you want. Because, you know, in order to create an object you need to know exactly which class it is... Does that make sense? So, I think it's pretty safe to say, you don't need a smart base builder. What you need is a concrete builder for each concrete class that needs to be created using a builder. Having a hierarchy of these builders is OK (as you pointed out, a base builder can contain some common logic). But it's fragile.

Cutting down the amount of code that needs to be written in the concrete builders and reusing logic from the base builder is closely tied to the domain. It's not easy to develop a general solution.

Let's go through an example:

public interface Builder<T> {   T build(); }  public class Person {   private String name;    Person(String name) {     this.name = name;   }    public String getName(){ return name; }    public static class PersonBuilder implements Builder<Person> {     private String name;     public void setName(String name){ this.name = name; }      public Person build() {       if(name == null) {         throw new IllegalArgumentException("Name must be specified");       }       return new Person(name);     }   } } 

Groovy, baby! Now what? Maybe you want to add a class to represent a student. What do you do? Do you extend Person? Sure, that's valid. How about taking a more "strange" route and attempting aggregation? Yep, you can do that too... Your choice would have an affect on how you will end up implementing builders. Let's say you stick to the traditional path and extend Person:

public class Student extends Person {   private final long id;    Student(long id, String name) {     super(name);     this.id = id;   }    public long getId(){ return id; } } 

What about the StudentBuilder? Well, do you extend PersonBuilder? If you do, you have to rewrite the definition of PersonBuilder (which is what you pointed out in your question):

  public static class PersonBuilder<P extends Person> implements Builder<P> {     private String name;     public void setName(String name){ this.name = name; }      public P build() {       if(name == null) {         throw new IllegalArgumentException("Name must be specified");       }       return /* ??? */;     }   } 

Now you have to make another decision - how do we create an instance of P if we don't know what P actually is? You decided to have a protected method buildHook in order to delegate object creation to the subclasses. OK. Let's explore that decision:

New version of the PersonBuilder

  public static class PersonBuilder<P extends Person> implements Builder<P> {     private String name;     public void setName(String name){ this.name = name; }      public P build() {       if(name == null) {         throw new IllegalArgumentException("Name must be specified");       }       P instance = createInstance();       instance.name = name       return instance;     }      protected abstract P createInstance();   } 

Now you need to write the StudentBuilder, so you start by writing:

 public static class StudentBuilder extends PersonBuilder<StudentBuilder> 

Well, immediately, questions should begin to arise. Doesn't this seem inconsistent? What if I end up needing to extend Student? Oops... That wouldn't work very well, now would it? Why? Because now StudentBuilder shouldn't create an instance of a Student, it should delegate that creation to a subclass, just like PersonBuilder delegated creation logic to its subclasses. How would you delegate that? Create another abstract method? But shouldn't Student be a concrete class? Now it's becoming a giant ball of WTF... When do we stop delegating? At some point you will need concrete classes.

Another point to consider is whether Person should be abstract. How about Student? Those two questions are impossible to answer without knowing the goal of the software...

I believe at this point the answer is domain-specific. You might think you know the whole hierarchy ahead of time, and you will never need to extend Student... unlikely... Because when the time comes to extend student (and it's likely to happen, or at least you should always plan for it to happen) your existing builder logic will break.

So, going back to what I said, builders should be very simple objects. The phrase hierarchy of builders should sound alarming. Re-using builder logic is not straightforward and creates tight-coupling of builders to the classes they're supposed to build. In the end, if you're worried about duplicating setters in the builders you're right, it sucks. What do you do? You can generate the setters using your IDE... Not really a solution, more along the lines of "my foot is turning more purple as the days pass... oh well, I'll just start taking more vicodin."

I think at this point, it would be good to explore the abstract factory pattern as an option. I realize that this isn't a direct answer to your question, but I'm not sure that one exists without knowing more about your requirements.

Also, take a look at a related question.

Source: http://stackoverflow.com/questions/13255985/builder-joshua-bloch-style-for-concrete-implementation-of-abstract-class

Pumpkin Carving Ideas Hurricane Sandy path opm daylight savings school closings chicago bears sandy

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.