Builder pattern for polymorphic object hierarchy: possible with Java?

后端 未结 6 847
谎友^
谎友^ 2020-12-08 01:11

I have a hierarchy of interfaces, with Child implementing Parent. I would like to work with immutable objects, so I would like to design Bui

6条回答
  •  自闭症患者
    2020-12-08 01:55

    I don't think getParent() and getThis() are necessary, if you're willing to accept the restriction that the withXXXProperty() methods be called from "youngest" to "oldest":

    class Parent
    {
        private final long parentProperty;
    
        public long getParentProperty()
        {
            return parentProperty;
        }
    
        public static abstract class Builder
        {
            private long parentProperty;
    
            public Builder withParentProperty( long parentProperty )
            {
                this.parentProperty = parentProperty;
                return this;
            }
    
            public abstract T build();
        }
    
        public static Builder builder()
        {
            return new Builder()
            {
                @Override
                public Parent build()
                {
                    return new Parent(this);
                }
            };
        }
    
        protected Parent( Builder builder )
        {
            this.parentProperty = builder.parentProperty;
        }
    }
    
    class Child1 extends Parent
    {
        private final int child1Property;
    
        public int getChild1Property()
        {
            return child1Property;
        }
    
        public static abstract class Builder extends Parent.Builder
        {
            private int child1Property;
    
            public Builder withChild1Property( int child1Property )
            {
                this.child1Property = child1Property;
                return this;
            }
    
            public abstract T build();
        }
    
        public static Builder builder()
        {
            return new Builder()
            {
                @Override
                public Child1 build()
                {
                    return new Child1(this);
                }
            };
        }
    
        protected Child1( Builder builder )
        {
            super(builder);
            this.child1Property = builder.child1Property;
        }
    
    }
    
    class Child2 extends Parent
    {
    
        private final String child2PropertyA;
        private final Object child2PropertyB;
    
        public String getChild2PropertyA()
        {
            return child2PropertyA;
        }
    
        public Object getChild2PropertyB()
        {
            return child2PropertyB;
        }
    
        public static abstract class Builder extends Parent.Builder
        {
            private String child2PropertyA;
            private Object child2PropertyB;
    
            public Builder withChild2PropertyA( String child2PropertyA )
            {
                this.child2PropertyA = child2PropertyA;
                return this;
            }
    
            public Builder withChild2PropertyB( Object child2PropertyB )
            {
                this.child2PropertyB = child2PropertyB;
                return this;
            }
        }
    
        public static Builder builder()
        {
            return new Builder()
            {
                @Override
                public Child2 build()
                {
                    return new Child2(this);
                }
            };
        }
    
        protected Child2( Builder builder )
        {
            super(builder);
            this.child2PropertyA = builder.child2PropertyA;
            this.child2PropertyB = builder.child2PropertyB;
        }
    }
    
    class BuilderTest
    {
        public static void main( String[] args )
        {
            Child1 child1 = Child1.builder()
                    .withChild1Property(-3)
                    .withParentProperty(5L)
                    .build();
    
            Child2 grandchild = Child2.builder()
                    .withChild2PropertyA("hello")
                    .withChild2PropertyB(new Object())
                    .withParentProperty(10L)
                    .build();
        }
    }
    

    There's still some boilerplate here: the anonymous concrete Builder in each builder() method, and the super() call in each constructor. (Note: that assumes that every level is designed for further inheritability. If at any point you have a final descendant, you can make the builder class concrete and the constructor private.)

    But I think this version is easier to follow, for the next programmer who comes along and has to maintain your code (no self-referential generics, for starters; a Builder builds Xs). And IMHO requiring the child properties to be set on the builder before the parent properties is as much an advantage, in terms of consistency, as it is a disadvantage in terms of flexibility.

提交回复
热议问题