1

I'm new to the art of designing fluent, extensible interfaces. I have a chain of builder classes that implement interfaces and both the interfaces and the builder implementations take self-referential type parameters.

Note: This is related to my previous question in which I had attempted to design the interfaces slightly differently before trying this approach in which the interfaces take a self-referential type parameter as well:

Here is the interface:

public interface ILoadableBuilder<C extends ILoadable,T extends ILoadableBean, B extends ILoadableBuilder<C,T,B>> {
    T getState();
    B setComponentClass(final Class<C> componentClass);
    B setDriver(final WebDriver driver);
    B setLoadTimeoutInSeconds(final @Nonnegative int loadTimeoutInSeconds);
    B setEnumerator(final IEnumeratorBean<? extends IEnumerable<?>,?> enumerator);
}

Here is an implementation, which also takes a self-referential type parameter. The reason the class implementation takes the parameter is that I want it to be extensible so that other builders can then extend this class and inherit all its behavior, and that the setters can be called in any order and the return type will be correct:

public class LoadableBuilder<C extends ILoadable,T extends ILoadableBean,B extends ILoadableBuilder<C,T,B>> implements
        ILoadableBuilder<C,T,B> {

    private final T componentBean;
    private IEnumeratorBean<? extends IEnumerable<?>,?> enumerator;
    private Class<C> componentClass;

    public LoadableBuilder(final T componentBean) {
        this.componentBean = componentBean;
    }

    public final T getState() {
        return componentBean;
    }

    public final B setComponentClass(final Class<C> componentClass) {
        this.componentClass = componentClass;
        return (B)this;
    }

    public final B setDriver(final WebDriver driver) {
        getState().setDriver(driver);
        return (B)this;
    }

    public final B setLoadTimeoutInSeconds(final int loadTimeoutInSeconds) {
        getState().getLoadTimeoutInSeconds();
        return (B)this;
    }

    public B setEnumerator(final IEnumeratorBean<? extends IEnumerable<?>,?> enumerator) {
        this.enumerator = enumerator;
        return (B)this;
    }
}

My question is, how the heck do you instantiate an instance of this implementation without having to pass a type parameter to the client class? Say, I want to declare a member variable in a class that uses the builder like so:

public ClientClass<C,T> {

    private ILoadableBuilder<C,T,_what do I put here????_> builder = new LoadableBuilder<C,T,_what do I put here?????_>();

}

For a method, it's no big deal because I can do this:

public <B extends ILoadableBuilder<C,T,B>> void useABuilder() {
    ILoadableBuilder<C,T,B> builder = new LoadableBuilder<C,T,B>();
}

EDIT:

ClientClass would want to build an object that implements the ILoadable interface. I have lots of builders that extend ILoadableBuilder to build objects that implement interfaces which are sub-types of ILoadable. The idea is that I want to be able to get a builder for any object in the inheritance hierarchy underneath ILoadable, which themselves are extensible where necessary.

Community
  • 1
  • 1
Selena
  • 2,208
  • 8
  • 29
  • 49
  • Depends on how are you going to use `builder` in `ClientClass`. – Rohit Jain Feb 23 '15 at 17:54
  • I just want to use it to build an object that needs parameters of the types which are specified in the builder object. Basically, I need to to build an object that implements ILoadable instead of an object that is of a subtype of ILoadable (which would require additional setters which are available in a builder that extends the LoadableBuilder). – Selena Feb 23 '15 at 17:56
  • I meant to say, how you want to use the `builder` instance you create in your `ClientClass`. – Rohit Jain Feb 23 '15 at 17:58
  • Like I said -- I want to be able to instantiate a LoadableBuilder in order to build an object that implements the ILoadable interface. – Selena Feb 23 '15 at 18:03
  • Well, you could just use: `ILoadableBuilder>` or `ILoadableBuilder>`. But as I said, that would depend upon how you are going to use the created instance. For example, you can't add anything to `List extends T>`, but you can add to `List>` or `List super T>`. The construction of a generic type completely depends upon your usage. – Rohit Jain Feb 23 '15 at 18:11
  • Interesting. Well, I haven't needed any List extends T> data structures. I will try out these approaches. – Selena Feb 23 '15 at 19:19
  • Why do you use casting instead of adding B builder as a field and delegating to it ? – janek Feb 23 '15 at 19:52

1 Answers1

0

You have two options:

  • Make LoadableBuilder not extensible, and just declare it as

    public class LoadableBuilder<C extends ILoadable, T extends ILoadableBean>
        implements ILoadableBuilder<C,T,LoadableBuilder<C,T>>
    
  • If you want to let it be extensible, then make it always necessary to extend it. Let LoadableBuilder be "abstract". Define a bare-bones implementation that just subclasses it with no extra stuff for the "basic" behavior:

    public abstract class LoadableBuilder<C extends ILoadable,
        T extends ILoadableBean, B extends ILoadableBuilder<C,T,B>>
        implements ILoadableBuilder<C,T,B> {
        //...
    }
    
    public class BasicLoadableBuilder<C extends ILoadable, T extends ILoadableBean>
        extends LoadableBuilder<C,T, BasicLoadableBuilder<C,T>> {
    }
    
newacct
  • 119,665
  • 29
  • 163
  • 224