Extending classes using Builder pattern

2019-02-11 01:09发布

问题:

I am trying to extend a class a, into aX. So, I also extend aBuilder. However, while I am able to create an object of class a using :

aBuilder f = new aBuilder();
f.bi = i;
f.bs = s;
a atry = f.withI(i).withS(s).build();

The same doesn't work for aX. When I try to do this :

aXBuilder fb = new aXBuilder();
aX aXtry = fb.withI(i).withS(s).withB(b).build();

I get an error (The method withB(Boolean) is undefined for the type a.aBuilder). Should I instead rewrite all the stuff for aX, instead of simply adding new stuff? I don't want to do that because that will lead to a lot of duplicacy in my code. The classes a and aX are as given below:

class a {

protected String s;
protected int i;

public void getdata() {
    System.out.println(this.s);
    System.out.println(this.i);
}

protected a(aBuilder fb) {
    this.s = fb.bs;
    this.i = fb.bi;
}

public static class aBuilder {
    public aBuilder() {
    }

    protected String bs;
    protected int bi;

    public aBuilder withS(String s) {
        this.bs = s;
        return this;
    }

    public aBuilder withI(Integer i) {
        this.bi = i;
        return this;
    }

    public a build() {
        return new a(this);
    }

}

}

class aX extends a {

protected Boolean b;

public void getData()
{
    System.out.println(this.s);
    System.out.println(this.i);
    System.out.println(this.b);
}

protected aX(aXBuilder axb) {
    super(axb);
    this.b = axb.bb;
}

public static class aXBuilder extends aBuilder {
    protected Boolean bb;

    public aXBuilder() {
    }

    public aXBuilder withB(Boolean b) {
        this.bb = b;
        return this;
    };

    public aX build() {
        return new aX(this);
    }
}

}

回答1:

You can solve your problem with generics, although it does require creation of an abstract superclass. Lurking on this site has taught me that inheriting from a concrete class is widely considered to be evil.

public abstract class AbstractA {
    protected String s;
    protected int i;
    protected AbstractA() {
    }
    protected abstract static class ABuilder<T extends AbstractA, B extends ABuilder<T,B>> {
        protected T object;
        protected B thisObject;
        protected abstract T getObject(); //Each concrete implementing subclass overrides this so that T becomes an object of the concrete subclass
        protected abstract B thisObject(); //Each concrete implementing subclass builder overrides this for the same reason, but for B for the builder
        protected ABuilder() {
            object = getObject();
            thisObject = thisObject();
        }
        public B withS(String s) {
            object.s = s;
            return thisObject;
        }
        public B withI(int i) {
            object.i = i;
            return thisObject;
        }
        public T build() {
            return object;
        }
    }
}

Once you have your abstract class together, you just extend it as many times as you need, overriding the abstract methods in the builder to return the type of object you need.

public final class ConcreteA extends AbstractA {
    private String foo;
    protected ConcreteA() {
    }
    public static final class Builder extends AbstractA.ABuilder<ConcreteA,Builder> {
        @Override protected ConcreteA getObject() {
            return new ConcreteA();
        }
        @Override protected Builder thisObject() {
            return this;
        }
        public Builder() {
        }
        public Builder withFoo(String foo) {
            object.foo = foo;
            return this;
        }
    }
}

Then... ConcreteA baz = new ConcreteA.Builder().withFoo("foo").withS("bar").withI(0).build();



回答2:

withS(s) returns aBuilder, aBuilder does not have a method withB.

Refactor required. Override withS etc in your sub class to return the correct type.

(You don't normally explicitly instantiate builders, just reference them statically btw, and also a as a classname is bad)