Java builder pattern - abstract builder

1.5k Views Asked by At

First of all, I'm relatively new to Java, so may be what I am asking is trivial, but I could not find an answer here or in other place.

For simplicity, let's assume I have the following class hierarchy:

class Shape {
    protected Shape(double x, double y) {...}
}

class Circle extends Shape {
    public Circle(double radius) {...}
}

class Rectangle extends Shape {
    public Rectangle(double edge) {...}
}

I'd like to use a builder pattern for each shape. So I added Builders to each one of them:

class Shape {
    protected static abstract class BuilderBase<T extends BuilderBase<T, S>, S extends Shape> {
        public T setLocation(double x, double y) {
            // ...
            return (T)this; // ? - is there a way to avoid this casting?
        }

        public abstract S build();
    }

    protected Shape(/*...*/) {/*...*/}
}

class Circle extends Shape {
    public static class Builder extends BuilderBase<Builder, Circle> {
        public Builder setRadius(double radius) {
            //...
            return this;
        }

        @Override
        public Circle build() { return new Circle(/*...*/); }
    }

    private Circle(/*...*/) {/*...*/}
}

class Rectangle extends Shape {
    public static class Builder extends BuilderBase<Builder, Rectangle> {
        public Builder setRadius(double radius) {
            //...
            return this;
        }

        @Override
        public Rectangle build() { 
            return new Rectangle(/*...*/); 
        }
    }

    public Rectangle(/*...*/) {/*...*/}
}

EDIT: Generic here was used in order to allow using Builder methods in any order. For example, in order to allow the following call:

new Circle.Builder()
    .setLocation(0, 0)
    .setRadius(10)
    .build();

My problem is in this casting:

public T setLocation(double x, double y) {
    // ...
    return (T)this; // ? - is there a way to avoid this casting?
}

I'm struggling to find a way to avoid this casting. The only way I found till now was to add yet another abstract method to the BaseBuilder method:

protected static abstract class BuilderBase<T extends BuilderBase<T, S>, S extends Shape> {
    protected abstract T getBuilder();

    public T setLocation(double x, double y) {
        // ...
        return getBuilder();
    }

    //...
}

So each derived builder will have to implement it:

@Override
protected Circle getBuilder() { 
    return this; 
}

It seems to me as overkill but I don't want to get compilation warnings.

Question: is there any more elegant way to avoid the casting?

1

There are 1 best solutions below

7
On BEST ANSWER

You can’t avoid having a little bit of additional code per subclass but you can avoid having an unchecked cast. Just create an abstract method responsible for returning an appropriately typed this. This method has to be implemented by concrete subclasses once but can be used for all methods of the base class which ought to return this:

class Shape {
  protected static abstract
  class BuilderBase<T extends BuilderBase<T, S>, S extends Shape> {
    /** all subclasses should implement {@code self()} as {@code return this;} */
    abstract T self();
    public T setLocation(double x, double y) {
        // ...
        return self();
    }
    public T setFooBar(FooBar x) {
      // ...
      return self();// the more methods you have the more useful self() becomes
  }

    public abstract S build();
  }

  protected Shape(/*...*/) {/*...*/}
}

class Circle extends Shape {
  public static class Builder extends BuilderBase<Builder, Circle> {
    @Override final Builder self() { return this; }

    public Builder setRadius(double radius) {
        //...
        return this;
    }

    @Override
    public Circle build() { return new Circle(/*...*/); }
  }

  private Circle(/*...*/) {/*...*/}
}

class Rectangle extends Shape {
  public static class Builder extends BuilderBase<Builder, Rectangle> {
    @Override final Builder self() { return this; }

    public Builder setRadius(double radius) {
      //...
      return this;
    }

    @Override
    public Rectangle build() { 
      return new Rectangle(/*...*/); 
    }
  }

  public Rectangle(/*...*/) {/*...*/}
}