Wednesday, July 6, 2016

Compile-time Builder Pattern in Java

Builder pattern is a pattern that is usually used for a language that does not support named parameters. However, the typical builder pattern has a limitation, such at it can't at compile-time check the required parameters. Below are some suggestions how to use a builder pattern that has compile-time check.
  • Option 1
  • public class Foo {
        private String a;
        private String b;
        private String c;
    
        private Foo() {
        }
    
        public static ABuilder newBuilder() {
            return new ABuilder(new Foo());
        }
    
        private static class ABuilder {
            private final Foo foo;
    
            private ABuilder(Foo foo) {
                this.foo = foo;
            }
    
            public BBuilder a(String a) {
                foo.a = a;
                return new BBuilder(foo);
            }
        }
    
        private static class BBuilder {
            private final Foo foo;
    
            private BBuilder(Foo foo) {
                this.foo = foo;
            }
    
            public CBuilder b(String b) {
                foo.b = b;
                return new CBuilder(foo);
            }
        }
    
        private static class CBuilder {
            private final Foo foo;
    
            private CBuilder(Foo foo) {
                this.foo = foo;
            }
    
            public FinalBuilder c(String c) {
                foo.c = c;
                return new FinalBuilder(foo);
            }
        }
    
        private static class FinalBuilder {
            private final Foo foo;
    
            private FinalBuilder(Foo foo) {
                this.foo = foo;
            }
    
            public Foo build() {
                return foo;
            }
        }
    
        public static void main(String[] args) {
            Foo foo = Foo.newBuilder().a("a").b("b").c("c").build();
            System.out.println(foo);
        }
    }
    
  • Option 2
  • public class Bar {
        private final String a;
        private final String b;
        private final String c;
    
        public static class False {}
        public static class True {}
    
        public static class Builder<Has1, Has2, Has3> {
            private String a;
            private String b;
            private String c;
    
            public static Builder<False, False, False> create() {
                return new Builder<>();
            }
    
            public Builder<True, Has2, Has3> a(String a) {
                this.a = a;
                return (Builder<True, Has2, Has3>) this;
            }
    
            public Builder<Has1, True, Has3> b(String b) {
                this.b = b;
                return (Builder<Has1, True, Has3>) this;
            }
    
            public Builder<Has1, Has2, True> c(String c) {
                this.c = c;
                return (Builder<Has1, Has2, True>) this;
            }
        }
    
        public Bar(Builder<True, True, True> builder) {
            a = builder.a;
            b = builder.b;
            c = builder.c;
        }
    
        public static void main(String[] args) {
            Bar bar = new Bar(Builder.create().a("a").b("c").c("a"));
            System.out.println(bar);
        }
    }