“This is the 11th day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”

Hello, I’m looking at the mountains.

Having talked about the singleton pattern, this week I want to talk about the Builder pattern, which is another commonly used creative design pattern.

Every design pattern arises to solve some problem of programming elegance, and the builder pattern is no different.

Builder Pattern, also known as generator Pattern, is an object construction Pattern. It can abstract the construction process of complex objects (abstract classes), so that different implementation methods of this abstract process can construct objects with different manifestations (attributes).

So let’s do an example

Borrow and transform the given in the Effective Java example: each kind of food will have a nutritional ingredients on the package, the content of each of the content, can, each calories, fat, carbohydrates and sodium, may also have other N kinds of optional data, most products of certain ingredients have value, how to define the nutrition composition of this class?

Overlapping constructor

Because there are multiple parameters, required, optional, the first to think of is to define multiple have arguments constructor: the first constructor only will pass parameters, and second, on the basis of the first constructor and an optional parameter, the third add two, and so on, until the last one contains all the parameters, this method is called overlapping constructor, a bit like a pyramid. Another common way to write this is to write a single constructor that contains all the arguments.

The code is as follows:

public class Nutrition {
    private int servingSize;// required
    private int servings;// required
    private int calories;// optional
    private int fat;// optional
    private int sodium;// optional
    private int carbohydrate;// optional

    public Nutrition(final int servingSize, final int servings) {
        this(servingSize, servings, 0.0.0.0);
    }

    public Nutrition(final int servingSize, final int servings, final int calories) {
        this(servingSize, servings, calories, 0.0.0);
    }

    public Nutrition(final int servingSize, final int servings, final int calories, final int fat) {
        this(servingSize, servings, calories, fat, 0.0);
    }

    public Nutrition(final int servingSize, final int servings, final int calories, final int fat, final int sodium) {
        this(servingSize, servings, calories, fat, sodium, 0);
    }

    public Nutrition(final int servingSize, final int servings, final int calories, final int fat, final int sodium, final int carbohydrate) {
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
        this.carbohydrate = carbohydrate;
    }

    // getter
}
Copy the code

This can also be done effectively by adding parameter validation to the constructor.

If you want to initialize the instance, you just need new: newNutrition(100,50,0,35,0,10). The less elegant part of this approach is that when calories and sodium are 0, you also need to explicitly define 0 in the constructor. In the example, only six parameters are acceptable. But what if there are 20 parameters? Only one of the optional arguments is either zero or null, which is fun to write, and the screen is full of zeros and null hybrids.

Has a hidden drawback, that is, if the same type parameters, such as the example above, is type int, unless contrast carefully each time you create an instance method signature, otherwise it’s easy to preach fault parameters, and the error editor to check out, is only at run time appear all sorts of strange errors, row wrong don’t know how to pull them off the hair.

To solve the above two problems, it is not difficult to imagine that the set method can be assigned one by one.

Set assignment

Since putting too many parameters in a constructor is not elegant and has its drawbacks, it is better to write the constructor in a different way, keeping only the necessary fields and using setters for all other parameter assignments.

The code is as follows:

public class Nutrition {
    private final int servingSize;// required
    private final int servings;// required
    private int calories;// optional
    private int fat;// optional
    private int sodium;// optional
    private int carbohydrate;// optional

    public Nutrition(int servingSize, int servings) {
        this.servingSize = servingSize;
        this.servings = servings;
    }

    // getter and setter
}
Copy the code

This solves the problem of having too many constructor arguments and passing the wrong ones, by specifying the set only when needed.

If there are no special needs, this is where most of the problems will be solved.

But the needs are always changing, and there are always weird requirements like “multicolored black” :

  1. If there are many required parameters, or most of the parameters are required. At this point, this approach has the disadvantages of overlapping constructors.

  2. If all parameters are assigned by set, there is no way to validate the required fields.

  3. If there is a correlation between non-mandatory parameters, for example, if there is value of fat and carbohydrate in the preceding example, calories must not be 0. But with the current design approach, there is no place to define dependencies between attributes or validation logic for constraints.

  4. If you want to define Nutrition as an immutable object, you cannot change the property values using the set method.

And that’s where today’s hero comes in.

Builder model

On the first code

public class Nutrition {
    private int servingSize;// required
    private int servings;// required
    private int calories;// optional
    private int fat;// optional
    private int sodium;// optional
    private int carbohydrate;// optional

    public static class Builder {
        private final int servingSize;// required
        private final int servings;// required
        private int calories;// optional
        private int fat;// optional
        private int sodium;// optional
        private int carbohydrate;// optional

        public Builder(final int servingSize, final int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }

        public Builder setCalories(final int calories) {
            this.calories = calories;
            return this;
        }

        public Builder setFat(final int fat) {
            this.fat = fat;
            return this;
        }

        public Builder setSodium(final int sodium) {
            this.sodium = sodium;
            return this;
        }

        public Builder setCarbohydrate(final int carbohydrate) {
            this.carbohydrate = carbohydrate;
            return this;
        }

        public Nutrition build(a) {
            // Define the validation logic for dependencies or constraints
            return new Nutrition(this); }}private Nutrition(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }

    // getter
}
Copy the code

To create an object, just call newNutrition.Builder(100,50).setfat (35).setluxurious (10).build(). This approach has the advantages of the first two approaches:

  • Can be unambiguous and unambiguoussetSpecify the value of the property;
  • inbuildMethod orNutritionThe constructor defines validation methods that can be performed during object creation.

The downside of the Builder pattern (which seems to be a common problem with all design patterns) is that it can be addressed by Lombok with the @Builder annotation, which automatically generates the Builder class of the object during compilation.

Let’s do another example

Let’s take a look at an example from The Big Talk Design Pattern. This example is very different from the Builder pattern in code structure, but the author classifies it as the Builder pattern. Now we need to draw a small person, a small person needs head, body, left hand, right hand, left foot, right foot.

The code is as follows:

public class Person {
    private String head;
    private String body;
    private String leftHand;
    private String rightHand;
    private String leftLeg;
    private String rightLeg;

    // getter/setter
}

public class PersonBuilder {
    private Person person = new Person();

    public PersonBuilder buildHead(a) {
        person.setHead("Head");
        return this;
    }

    public PersonBuilder buildBody(a) {
        person.setBody("Body");
        return this;
    }

    public PersonBuilder buildLeftHand(a) {
        person.setLeftHand("The left");
        return this;
    }

    public PersonBuilder buildRightHand(a) {
        person.setRightHand("Right hand");
        return this;
    }

    public PersonBuilder buildLeftLeg(a) {
        person.setLeftLeg("Left");
        return this;
    }

    public PersonBuilder buildRightLeg(a) {
        person.setRightLeg("Right");
        return this;
    }

    public Person getResult(a) {
        return this.person; }}Copy the code

However, if there is a method forget to call, such as drawing the right hand method forget to call, that is Yang Guo warrior. At this point, you need to add a Director class to the PersonBuilder.

public class PersonDirector {
    private final PersonBuilder pb;

    public PersonDirector(final PersonBuilder pb) {
        this.pb = pb;
    }

    public Person createPerson(a) {
        this.pb
            .buildHead()
            .buildBody()
            .buildLeftHand()
            .buildRightHand()
            .buildLeftLeg()
            .buildRightLeg();
        return this.pb.getResult(); }}Copy the code

At this point, the client only needs to focus on the Director class, which is equivalent to adding a supervisor, a docking person, between the client calls to the constructor to ensure that the client can use the Builder class correctly.

For example, if you need to add a FatPersonBuilder class, you just define a FatPersonBuilder. Inherit the PersonBuilder, and then simply pass the newly added class into the Director’s constructor.

This is another advantage of the Builder pattern: you can define different Builder classes to implement different build properties, such as the Normal and Fat Builder classes above.

Finally, a summary

Some friends may say, these two examples have very different structures, how can they be the same model?

Let’s take a look at the official constructor mode class diagram:

Both examples contain the Product and Builder classes (or subclasses). The difference is that in the first example, object integrity is entrusted to the client. In the second example, object integrity is guaranteed by the Director class.

Let’s take a look at the nature of the builder: building stateless, complex objects.

  • Complex structure: If there are only a few attributes, the constructor can be implemented. If there are more attributes and the structure is complex, the Builder mode can be valuable. It is recommended to use the Builder mode when there are more than six attributes.

  • State integrity: State integrity can be managed by the client or Director. It does not occur because forgetting to call set or other methods means that the object has one less property defined.

In a sense, the builder pattern emerged to compensate for the shortcomings of constructors, with the following three major advantages:

  1. Encapsulate the creation of a complex object to hide the internal representation of the product from the client
  2. Allows objects to be created in multiple steps and can change the process
  3. The implementation of the product can change because the client only sees an abstract interface

As a practical design mode, builder mode is mainly applied in the following two scenarios:

  • When algorithms that create complex objects should be independent of the components of the object and how they are assembled
  • When the construction process must allow different representations of the object being constructed

In both cases, don’t hesitate to use the Builder mode.

Due to the impact of the epidemic, this year’s recruitment and application has become very different from previous years. The original period of three silver coins and four silver coins may be extended to May and June, so that people can have more time to prepare. However, there is also a disadvantage for candidates. As the preparation time lengthens, whether the basic skills are solid or not will make the interviewer’s performance distance. Design mode is one of the basic skills. Therefore, I plan to launch the series of “Design Mode for interview” recently, hoping to consolidate my foundation and help more people.

Recommended reading

  • The singleton pattern
  • Builder model

This article was picked up by Java Advancements.


Hello, I’m looking at the mountains. Swim in the code, play to enjoy life. If this article is helpful to you, please like, bookmark, follow. Welcome to follow the public account “Mountain Hut”, discover a different world.