5

I am currently using the Builder pattern, following closely the Java implementation suggested in the Wikipedia article Builder pattern http://en.wikipedia.org/wiki/Builder_pattern

This is a sample code that ilustrates my implementation

public class MyPrimitiveObject {
  private String identifier="unknown";
  public static class Builder {
    private final MyPrimitiveObject obj = new MyPrimitiveObject();
    public MyPrimitiveObject build() { return obj; }
    public Builder setidentifier (String val) {
     obj.identifier = val;
     return this;
    }
  }
  public static Builder createBuilder() { return new Builder(); }
  @Override public String toString() { return "ID: "+identifier; }
}

In some of my applications that use this class, I happen to find very similar building code , so I thought to subclass MyPrimitiveObject in MySophisticatedObject and move all my repeated code into its constructor.. and here is the problem.

How may I invoke the superclass Builder and assign its returned object as my instance?

public class MySophisticatedObject extends MyPrimitiveObject {
  private String description;
  public MySophisticatedObject (String someDescription) {
    // this should be the returned object from build() !!
    Builder().setidentifier(generateUUID()).build()
    description = someDescription;
  }     
}
PA.
  • 28,486
  • 9
  • 71
  • 95
  • Here is the long version of how you could do it: http://weblogs.java.net/node/642849 - I generally use the static factory approach – assylias Apr 10 '12 at 16:22
  • @assylias, thanks; I already found it before posting but this does not answer my question. It's not that I want to subclass and publish a new Builder for my subclass, I just want to use the already existing superclass builder in my constructor. Is it possible? – PA. Apr 10 '12 at 16:26
  • I misread your question. – assylias Apr 10 '12 at 16:27

2 Answers2

6

You might want to consider having a nested MySophisticatedObject.Builder which extends MyPrimitiveObject.Builder, and overrides its build() method. Have a protected constructor in the builder to accept the instance on which to set values:

public class MyPrimitiveObject {
  private String identifier="unknown";
  public static class Builder {
    private final MyPrimitiveObject obj;
    public MyPrimitiveObject build() { return obj; }
    public Builder setidentifier (String val) {
     obj.identifier = val;
     return this;
    }

    public Builder() {
        this(new MyPrimitiveObject());
    }

    public Builder(MyPrimitiveObject obj) {
        this.obj = obj;
    }
  }
  ...
}

public class MySophisticatedObject extends MyPrimitiveObject {
  private String description;

  public static class Builder extends MyPrimitiveObject.Builder {
    private final MySophisticatedObject obj;
    public Builder() {
      this(new MySophisticatedObject());
      super.setIdentifier(generateUUID());
    }     
    public Builder(MySophisticatedObject obj) {
      super(obj);
      this.obj = obj;
    }

    public MySophisticatedObject build() {
      return obj;
    }

    // Add code to set the description etc.
  }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • thanks, but then I need to use the Builder when creating an instance of `MySophisticatedObject`, something I would like to avoid moving the creation code into the constructor. – PA. Apr 10 '12 at 16:29
  • @PA.: Well you'd certainly have to change the MyPrimitiveObject.Builder code to accept an existing instance anyway... at which point your MySophisticatedObject constructor *could* pass in `this` - but it feels a bit of a mess. It's odd to have a mixture of builder and non-builder in the same type hierarchy. You might want to consider using the builder approach I outlined, but creating a static method in MySophisticedObject to create an appropriate instance easily. – Jon Skeet Apr 10 '12 at 16:31
  • I see, let me think about it. I wanted to simplify the applicatio code to just a `new` instruction. – PA. Apr 10 '12 at 16:34
  • I accept your answer as it responds to my original post, however, I discovered another problem. The thing is that for some of the values I need to use for initialization of the superclass, I need to construct the subclass first. Imagine in the example that generateUUID() need some "new generator()" that must be a member of the subclassed object. If you believe posting the question as is in a comment, I might end up posting as a new Question. – PA. Apr 11 '12 at 06:28
  • @PA.: It's not really clear what you mean, to be honest... you can do what you like within the builder subclass. – Jon Skeet Apr 11 '12 at 06:32
  • in the building task I need to use an object that is a member of the subclass object itself, I doubt I can do it in the builder subclass. – PA. Apr 11 '12 at 07:15
  • @PA.: Have you tried it? It should be okay, so long as you refer to it via `obj`... – Jon Skeet Apr 11 '12 at 07:26
1

You need:

public class MySophisticatedObject extends MyPrimitiveObject {
  private String description;

  public static class SofisitcatedBuilder extends Builder {
    private final MySophisticatedObject obj = new MySophisticatedObject();
    public MyPrimitiveObject build() { return obj; }
    public Builder setDescription(String val) {
     obj.description = val;
     return this;
    }
  }

  public MySophisticatedObject (String someDescription) {
    // this should be the returned object from build() !!
    return new SofisitcatedBuilderBuilder()
         .setDescription(someDescription)
         .setidentifier(generateUUID()).build()
  }     
}
Eugene Retunsky
  • 13,009
  • 4
  • 52
  • 55