Builder Pattern and Inheritance

后端 未结 8 465
攒了一身酷
攒了一身酷 2020-11-29 19:01

I have an object hierarchy that increases in complexity as the inheritance tree deepens. None of these are abstract, hence, all of their instances serve a, more or less soph

相关标签:
8条回答
  • 2020-11-29 19:40

    This form seems to nearly work. It is not very tidy but it looks like it avoids your issues:

    class Rabbit<B extends Rabbit.Builder<B>> {
    
        String name;
    
        public Rabbit(Builder<B> builder) {
            this.name = builder.colour;
        }
    
        public static class Builder<B extends Rabbit.Builder<B>> {
    
            protected String colour;
    
            public B colour(String colour) {
                this.colour = colour;
                return (B)this;
            }
    
            public Rabbit<B> build () {
                return new Rabbit<>(this);
            }
        }
    }
    
    class Lop<B extends Lop.Builder<B>> extends Rabbit<B> {
    
        float earLength;
    
        public Lop(Builder<B> builder) {
            super(builder);
            this.earLength = builder.earLength;
        }
    
        public static class Builder<B extends Lop.Builder<B>> extends Rabbit.Builder<B> {
    
            protected float earLength;
    
            public B earLength(float earLength) {
                this.earLength = earLength;
                return (B)this;
            }
    
            @Override
            public Lop<B> build () {
                return new Lop<>(this);
            }
        }
    }
    
    public class Test {
    
        public void test() {
            Rabbit rabbit = new Rabbit.Builder<>().colour("White").build();
            Lop lop1 = new Lop.Builder<>().earLength(1.4F).colour("Brown").build();
            Lop lop2 = new Lop.Builder<>().colour("Brown").earLength(1.4F).build();
            //Lop.Builder<Lop, Lop.Builder> builder = new Lop.Builder<>();
        }
    
        public static void main(String args[]) {
            try {
                new Test().test();
            } catch (Throwable t) {
                t.printStackTrace(System.err);
            }
        }
    }
    

    Although I have successfully built Rabbit and Lop (in both forms) I cannot at this stage work out how to actually instantiate one of the Builder objects with it's full type.

    The essence of this method relies on the cast to (B) in the Builder methods. This allow you to define the type of object and the type of the Builder and retain that within the object while it is constructed.

    If anyone could work out the correct syntax for this (which is wrong) I would appreciate it.

    Lop.Builder<Lop.Builder> builder = new Lop.Builder<>();
    
    0 讨论(0)
  • 2020-11-29 19:47

    I did some experimenting and I found this to work quite nicely for me. Note that I prefer to create the actual instance at the start and the call all the setters on that instance. This is just a preference.

    The main differences with the accepted answer is that

    1. I pass a parameter that indicated the return type
    2. There is no need for an Abstract... and a final builder.
    3. I create a 'newBuilder' convenience method.

    The code:

    public class MySuper {
        private int superProperty;
    
        public MySuper() { }
    
        public void setSuperProperty(int superProperty) {
            this.superProperty = superProperty;
        }
    
        public static SuperBuilder<? extends MySuper, ? extends SuperBuilder> newBuilder() {
            return new SuperBuilder<>(new MySuper());
        }
    
        public static class SuperBuilder<R extends MySuper, B extends SuperBuilder<R, B>> {
            private final R mySuper;
    
            public SuperBuilder(R mySuper) {
                this.mySuper = mySuper;
            }
    
            public B withSuper(int value) {
                mySuper.setSuperProperty(value);
                return (B) this;
            }
    
            public R build() {
                return mySuper;
            }
        }
    }
    

    and then a subclass look like this:

    public class MySub extends MySuper {
        int subProperty;
    
        public MySub() {
        }
    
        public void setSubProperty(int subProperty) {
            this.subProperty = subProperty;
        }
    
        public static SubBuilder<? extends MySub, ? extends SubBuilder> newBuilder() {
            return new SubBuilder(new MySub());
        }
    
        public static class SubBuilder<R extends MySub, B extends SubBuilder<R, B>>
            extends SuperBuilder<R, B> {
    
            private final R mySub;
    
            public SubBuilder(R mySub) {
                super(mySub);
                this.mySub = mySub;
            }
    
            public B withSub(int value) {
                mySub.setSubProperty(value);
                return (B) this;
            }
        }
    }
    

    and a subsub class

    public class MySubSub extends MySub {
        private int subSubProperty;
    
        public MySubSub() {
        }
    
        public void setSubSubProperty(int subProperty) {
            this.subSubProperty = subProperty;
        }
    
        public static SubSubBuilder<? extends MySubSub, ? extends SubSubBuilder> newBuilder() {
            return new SubSubBuilder<>(new MySubSub());
        }
    
        public static class SubSubBuilder<R extends MySubSub, B extends SubSubBuilder<R, B>>
            extends SubBuilder<R, B> {
    
            private final R mySubSub;
    
            public SubSubBuilder(R mySub) {
                super(mySub);
                this.mySubSub = mySub;
            }
    
            public B withSubSub(int value) {
                mySubSub.setSubSubProperty(value);
                return (B)this;
            }
        }
    
    }
    

    To verify it fully works I used this test:

    MySubSub subSub = MySubSub
            .newBuilder()
            .withSuper (1)
            .withSub   (2)
            .withSubSub(3)
            .withSub   (2)
            .withSuper (1)
            .withSubSub(3)
            .withSuper (1)
            .withSub   (2)
            .build();
    
    0 讨论(0)
提交回复
热议问题