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
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    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
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    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);
        }
    }