How to implement the builder pattern in Java 8?

前端 未结 6 2088
忘了有多久
忘了有多久 2020-12-04 08:58

Implement the builder pattern prior to Java 8 has lots of tedious, nearly duplicated code; the builder itself is typically boilerplate code. Some duplicate code detectors co

6条回答
  •  醉话见心
    2020-12-04 09:19

    I have recently tried to revisit the builder pattern in Java 8, and I am currently using the following approach:

    public class Person {
    
        static public Person create(Consumer buildingFunction) {
            return new Person().build(buildingFunction);
        }
    
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    
        private Person() {
    
        }
    
        private Person build(Consumer buildingFunction) {
            buildingFunction.accept(new PersonBuilder() {
    
                @Override
                public PersonBuilder withName(String name) {
                    Person.this.name = name;
                    return this;
                }
    
                @Override
                public PersonBuilder withAge(int age) {
                    Person.this.age = age;
                    return this;
                }
            });
    
            if (name == null || name.isEmpty()) {
                throw new IllegalStateException("the name must not be null or empty");
            }
    
            if (age <= 0) {
                throw new IllegalStateException("the age must be > 0");
            }
    
            // check other invariants
    
            return this;
        }
    }
    
    public interface PersonBuilder {
    
        PersonBuilder withName(String name);
    
        PersonBuilder withAge(int age);
    }
    

    Usage:

    var person = Person.create(
        personBuilder -> personBuilder.withName("John Smith").withAge(43)
    );
    

    Advantages:

    • A clean builder interface
    • Little to no boilerplate code
    • The builder is well encapsulated
    • It's easy to segregate the optional attributes from the mandatory attributes of the target class (the optional attributes are specified in the builder)
    • No setter needed in the target class (in DDD, you generally don't want setters)
    • Use of a static factory method to create an instance of the target class (instead of using the new keyword, so it's possible to have several static factory methods, each with a meaningful name)

    Possible drawbacks:

    • The calling code can save a reference to the passed-in builder and later screw up the mounted instance, but who will do that?
    • If the calling code saves a reference to the passed-in builder, a memory leak can occur

    Possible alternative:

    We can setup a constructor with a building function, as follows:

    public class Person {
    
        static public Person create(Consumer buildingFunction) {
            return new Person(buildingFunction);
        }
    
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    
        private Person(Consumer buildingFunction) {
            buildingFunction.accept(new PersonBuilder() {
    
                @Override
                public PersonBuilder withName(String name) {
                    Person.this.name = name;
                    return this;
                }
    
                @Override
                public PersonBuilder withAge(int age) {
                    Person.this.age = age;
                    return this;
                }
            });
    
            if (name == null || name.isEmpty()) {
                throw new IllegalStateException("the name must not be null or empty");
            }
    
            if (age <= 0) {
                throw new IllegalStateException("the age must be > 0");
            }
    
            // check other invariants
        }
    }
    

提交回复
热议问题