Builder subclass pattern with twist in Java

I would like to create an abstract Builder for an abstract class (although it shouldn't be abstract) and each subclass of the abstract class can have its own Builder subclass. I also want every field / attribute to be filled with AKA required. So I am using Builder Patter With Twist ( https://blog.jayway.com/2012/02/07/builder-pattern-with-a-twist/ ).

I ran into a problem that was solved in this question I asked earlier: Generic parent cannot be returned as child without casting But now I cannot create multiple concrete / subclass collectors.

In the end, I would like to create objects something like this:

ConcreteBuilderA.getBuilder().setValue(Object value).setConcreteValue(int num).build()

      

If setValue () is owned by AbstractBuilder and others for specificBuilder.

My best shot was (greatly simplified and abstracted):

/**
* @param<B> the type of builded object it should return.
* @param<S> the type of the builder subclass.
* @param<L> the type of the linking interface.
*/
public abstract class AbstractBuilder<B extends AbstractClass, S extends AbstractBuilder, L> implements ValueSetter<L>
{
    protected B buildable;
    @Override
    public L setValue(Object value)
    {
         //set the value
         return this;//<-- returns Object, blocking the use of the ConcreteBuilder methods
    }
    public abstract B build();
}

      

|

public class ConcreteBuilder extends AbstractBuilder<ConcreteProduct, ConcreteBuilder, ConcreteValueSetter> implements ConcreteValueSetter
{
    @Override
    public ConcreteBuilder setConcreteValue(int num)
    {
        //set value
        return this;
    }
    @Override
    public ConcreteProduct build();
    {
        return buildable;
    }
}

      

|

public interface ValueSetter<L>
{
     public L setValue(Object value);
}

      

|

public interface ConcreteValueSetter
{
    public ConcreteBuilder setConcreteValue(int num);
}

      

As noted, this stops the chaining when it "switches" to the subclass's build methods. I've made several variations of this and I can't seem to get it to work.

So I'm really wondering if this is possible. If so, I would like to see how to do it. If this is not the case, I would like to know some technique that suits my requirements.

Thanks in advance!

+3


source to share


2 answers


I discovered the answer with the help of Federico Peralta Schaffner. It is likely that I made the developer in my real project difficult. So here is the code for Builder-with-a-twist + inheritance:

/**
 *
 * @param <P> the type of the product.
 * @param <L> the linking interface.
 */
public class AbstractBuilder<P extends AbstractClass, L> implements ValueSetterOne<L>, ValueSetterTwo<L>{

    protected P toBeBuild;
    @Override
    public L setValueTwo(int value) {
        //set value
        return (L) this;
    }
    @Override
    public ValueSetterTwo<L> setValueOne(int value){
        //set value
        return this;
}

      

|



public class ConcreteBuilder extends AbstractBuilder<ConcreteClass, NameSetter> implements NameSetter, Optional{
    public static ValueSetter<NameSetter> getBuilder()
    {
        AbstractBuilder<ConcreteClass, NameSetter> builder = new ConcreteBuilder();
        builder.toBeBuild = new ConcreteClass();
        return builder;
    }

    @Override
    public Optional buildName(String name) {
        this.toBeBuild.setCharacterName(name);
        return this;
    }

    @Override
    public ConcreteClass build() {
        return this.toBeBuild;
    }

    @Override
    public Optional addExtraObject(Object extra) {
        System.out.println("test");
        return this;
    }
}

      

|



public interface ValueSetterOne<L> {
    public ValueSetterTwo<L> setValueOne(int value);
}

      

|



public interface ValueSetterTwo<L> {
    public L setValue(int value);
}

      

|



public interface NameSetter {
    public Optional buildName(String name);
}

      

|



public interface Optional {
    public ConcreteClass build();
    public Optional addExtraObject(Object extra);
}

      

And then, to test this: ConcreteBuilder.getBuilder().setValueOne(0).setValueTwo(1).buildName("tricky").addExtraObject(args).build();

+1


source


Your problem is a symptom of lazy programming. Try it; pretend you are a professional software developer:



ConcreteBuilderA builder = ConcreteBuilderA.getBuilder();
ThingBeingBuilt thing;

builder.setValue(value);
builder.setConcreteValue(num);
thing = builder.build()

      

+1


source







All Articles