03. Builder Pattern Design Ideas
Catalog Introduction
- 01. Introduction to the Builder Model
- 1.1 Origin of the Builder Pattern
- 1.2 Builder Pattern Definitions
- 1.3 Builder model scenarios
- 1.4 Reflections on the Builder Model
- 02. Builder pattern implementation
- 2.1 List a scenario
- 2.2 Creation of object disadvantage scenarios
- 2.3 Analysis of case evolution
- 2.4 Understanding builders with examples
- 03. Analysis of the builder's model
- 3.1 Structure of the Builder Pattern
- 3.2 Builder pattern timing diagram
- 3.3 Basic Code Implementation
- 04. Builder's Case Practice
- 4.1 Building a House Case Development
- 4.2 General house development
- 4.3 Constructor optimization for building a house
- 05. Builder Model Expansion
- 5.1 Can builders simplify?
- 5.2 Differences from the Factory Pattern
- 06. Analysis of the advantages and disadvantages of builders
- 6.1 What are the advantages?
- 6.2 Points of deficiency analysis
- 07. Constructor Pattern Summary
- 7.1 Summary of the model
- 7.2 More content recommendations
01. Introduction to the Basics of Builder Mode
1.0 AI-generated blog summaries
This article describes in detail the design ideas of the builder pattern and its application scenarios. The main content includes the origin, definition, application scenarios and thinking of the builder pattern, and explains how to use the builder pattern to solve the problem of creating complex objects through examples. The article also compares the difference between the Builder Pattern and the Factory Pattern, and analyzes the advantages and disadvantages of the Builder Pattern. Finally, several links to related resources are provided to help readers gain a deeper understanding and application of design patterns.
1.1 Origin of the Builder Pattern
1. Both in the real world and in software systems, there are complex objects that have multiple components.
As in the case of an automobile, it consists of various components such as wheels, steering wheel, transmitter, etc.. For most users, they do not need to know the details of the assembly of these parts, and hardly ever use a single part, but rather a complete automobile, which can be designed and described through the builder model.
builder patternIt is possible to create a complex object step by step by separating the part from its assembly process. The user only needs to specify the type of the complex object to get the object without knowing the specific details of its internal construction.
2. Complex objects are equivalent to a car to be built, and the attributes of the object are equivalent to the parts of the car, and the process of building the product is equivalent to the process of combining the parts.
Because of the complexity of the process of combining components, the process of combining these components is often "externalized" to an object called a builder.
The builder returns to the client a complete product object that has already been built, and the user doesn't have to care about the attributes the object contains and how they are assembled, which is the pattern motivation for the builder pattern.
1.2 Builder Pattern Definitions
builder pattern(Builder Pattern): Separate the construction of a complex object from its representation, so that the same construction process can create different representations.
The builder pattern is a step-by-step creation of a complex object, which allows the user to build complex objects by specifying only their type and content; the user doesn't need to know the specific details of the internal construction.
The builder pattern belongs to the object creation pattern. Depending on the Chinese translation, thebuilder patternIt can also be referred to as the generator pattern.
1.3 Builder model scenarios
The builder pattern can be used in the following cases:
- The product objects that need to be generated have complex internal structures, and these product objects usually contain multiple member attributes.
- The attributes of the product objects that need to be generated depend on each other, and the order in which they are generated needs to be specified.
- The process of creating an object is independent of the class that created it. In thebuilder patternThe conductor class was introduced in to encapsulate the creation process in the conductor class and not in the builder class.
- Isolates the creation and use of complex objects and enables the same creation process to create different products.
1.4 Reflections on the Builder Model
The principle and code implementation of the builder pattern is very simple, it is not difficult to master, the difficulty lies in the application of the scene.
- For example, have you ever considered the question: why do you need the builder pattern to create objects when you can create them directly with the constructor or with the set method?
- Both the builder pattern and the factory pattern create objects, so what is the difference between the two of them?
02. Principles and Implementation of the Builder Pattern
2.1 List a scenario
In normal development, the most common way to create an object is to call the class constructor using the new keyword.
My question is, under what circumstances does this approach cease to be applicable, and does it require the use of abuilder patternto create objects? You can start by thinking about it, and I'll walk you through an example below.
Suppose there is a design interview question like this:
We need to define a resource pool configuration class ResourcePoolConfig. Here, you can simply understand the resource pool as a thread pool, a connection pool, an object pool, and so on. In this ResourcePoolConfig class, there are the following member variables, which are the configurable items. Now, please write code to implement this ResourcePoolConfig class.
2.2 Creation of object disadvantage scenarios
The most common and easy to think of implementation idea is shown in the code below.
public class BuilderDesign1 {
public static void main(String[] args) {
ResourcePoolConfig resourcePoolConfig = new ResourcePoolConfig("", 1, 2, 3);
}
public static class ResourcePoolConfig {
private static final int DEFAULT_MAX_TOTAL = 8;
private static final int DEFAULT_MAX_IDLE = 8;
private static final int DEFAULT_MIN_IDLE = 0;
private String name;
private int maxTotal = DEFAULT_MAX_TOTAL;
private int maxIdle = DEFAULT_MAX_IDLE;
private int minIdle = DEFAULT_MIN_IDLE;
public ResourcePoolConfig(String name, int maxTotal, int maxIdle, int minIdle) {
= name;
if (maxTotal <= 0) {
throw new IllegalArgumentException("maxTotal should be positive.");
}
= maxTotal;
if (maxIdle < 0) {
throw new IllegalArgumentException("maxIdle should not be negative.");
}
= maxIdle;
if (minIdle < 0) {
throw new IllegalArgumentException("minIdle should not be negative.");
}
= minIdle;
}
//...an omissiongettermethodologies...
}
}
Now, ResourcePoolConfig has only 4 configurable items, which corresponds to only 4 parameters in the constructor, which is a small number of parameters.
However, if the number of configurable items gradually increases to 8, 10, or even more, then continuing with the current design idea, the argument list of the constructor will become very long, and the code will become less readable and easier to use.
When using constructors, it's easy to get the order of the arguments wrong and pass in the wrong values, leading to very insidious bugs.
// Too many parameters, resulting in poor readability and possible mis-passing of parameters
ResourcePoolConfig config = new ResourcePoolConfig("dbconnectionpool", 16, null, 8, null, false , true, 10, 20, false, true);
2.3 Analysis of case evolution
The solution to this problem, which you should have already thought of, is to use the set() function to assign values to member variables in place of the lengthy constructor.
Let's look at the code directly, as shown below. Among them, the configuration item name is required, so we put it into the constructor settings, forced to fill in when creating class objects.
The other configuration items maxTotal, maxIdle, minIdle are not mandatory, so we set them with the set() function, so that users can choose to fill in or not.
public static class ResourcePoolConfig {
private static final int DEFAULT_MAX_TOTAL = 8;
private static final int DEFAULT_MAX_IDLE = 8;
private static final int DEFAULT_MIN_IDLE = 0;
private String name;
private int maxTotal = DEFAULT_MAX_TOTAL;
private int maxIdle = DEFAULT_MAX_IDLE;
private int minIdle = DEFAULT_MIN_IDLE;
public ResourcePoolConfig(String name) {
= name;
}
public void setMaxTotal(int maxTotal) {
if (maxTotal <= 0) {
throw new IllegalArgumentException("maxTotal should be positive.");
}
= maxTotal;
}
public void setMaxIdle(int maxIdle) {
if (maxIdle < 0) {
throw new IllegalArgumentException("maxIdle should not be negative.");
}
= maxIdle;
}
public void setMinIdle(int minIdle) {
if (minIdle < 0) {
throw new IllegalArgumentException("minIdle should not be negative.");
}
= minIdle;
}
//...an omissiongettermethodologies...
}
Next, let's see how the new ResourcePoolConfig class should be used. I have written a sample code as shown below. Without long function calls and argument lists, the code is much more readable and easier to use.
// ResourcePoolConfigExamples of use
ResourcePoolConfig config = new ResourcePoolConfig("dbconnectionpool");
(16);
(8);
At this point, we still haven't used thebuilder patternIf you want to use this method, you can use the constructor to set the required fields and the set() method to set the optional configurations to fulfill our design requirements.
- When setting object properties in set mode, there is an intermediate state, and there is a backward and forward order constraint on property validation, so the code for logical validation can't find a suitable place to put it.
- The set method also breaks the confidentiality of immutable objects. That is, once an object has been created, it can no longer modify the values of its internal properties. To accomplish this, we cannot expose the set() method in the ResourcePoolConfig class.
2.4 Understanding builders with examples
You can place the validation logic in the Builder class, create the builder first, and set the builder's variable values with the set() method, then do a centralized validation before actually creating the object with the build() method, and only create the object after the validation passes.
In addition to this, we change the constructor of ResourcePoolConfig to be private private. This way we can only create ResourcePoolConfig class objects through the builder.
Moreover, ResourcePoolConfig does not provide any set() method, so the object we create is immutable.
expense or outlaybuilder patternThe above requirement was re-implemented with the code shown below:
public class ResourcePoolConfig {
private String name;
private int maxTotal;
private int maxIdle;
private int minIdle;
private ResourcePoolConfig(Builder builder) {
= ;
= ;
= ;
= ;
}
//...an omissiongettermethodologies...
//we willBuilderThe class is designed as aResourcePoolConfiginner class of。
//We can also set theBuilderClasses are designed to be independent and non-internalResourcePoolConfigBuilder。
public static class Builder {
private static final int DEFAULT_MAX_TOTAL = 8;
private static final int DEFAULT_MAX_IDLE = 8;
private static final int DEFAULT_MIN_IDLE = 0;
private String name;
private int maxTotal = DEFAULT_MAX_TOTAL;
private int maxIdle = DEFAULT_MAX_IDLE;
private int minIdle = DEFAULT_MIN_IDLE;
public ResourcePoolConfig build() {
// The calibration logic is put here to do,Includes required field checking、Dependency validation、Constraint calibration, etc.
if ((name)) {
throw new IllegalArgumentException("...");
}
if (maxIdle > maxTotal) {
throw new IllegalArgumentException("...");
}
if (minIdle > maxTotal || minIdle > maxIdle) {
throw new IllegalArgumentException("...");
}
return new ResourcePoolConfig(this);
}
public Builder setName(String name) {
if ((name)) {
throw new IllegalArgumentException("...");
}
= name;
return this;
}
public Builder setMaxTotal(int maxTotal) {
if (maxTotal <= 0) {
throw new IllegalArgumentException("...");
}
= maxTotal;
return this;
}
public Builder setMaxIdle(int maxIdle) {
if (maxIdle < 0) {
throw new IllegalArgumentException("...");
}
= maxIdle;
return this;
}
public Builder setMinIdle(int minIdle) {
if (minIdle < 0) {
throw new IllegalArgumentException("...");
}
= minIdle;
return this;
}
}
}
// This code throws theIllegalArgumentException,on account ofminIdle>maxIdle
ResourcePoolConfig config = new ()
.setName("dbconnectionpool")
.setMaxTotal(16)
.setMaxIdle(10)
.setMinIdle(12)
.build();
In fact, creating objects using the builder pattern also prevents them from having invalid state. Let me explain this with another example.
For example, if we define a rectangular class, if we don't use the builder pattern and create first and then set, it will result in the object being invalid after the first set. The code is shown below:
Rectangle r = new Rectange(); // r is invalid
(2); // r is invalid
(3); // r is valid
To avoid this invalid state, we need to initialize all the member variables at once using the constructor.
If the constructor has too many arguments, we need to consider using the builder pattern, which sets the builder's variables first and then creates the object once and for all, so that the object is always in a valid state.
In fact, if we don't really care if the object has a brief invalid state or if the object is mutable. For example, if the object is just used to map data read from a database, it's perfectly fine to just expose the set() method to set the value of a class member variable.
Also, using the builder pattern to build objects, the code is actually a bit repetitive, with member variables in the ResourcePoolConfig class having to be redefined again in the Builder class.
03. Analysis of the builder's model
3.1 Structure of the Builder Pattern
The builder pattern contains the following roles:
- Builder: Abstract Builder
- ConcreteBuilder: concrete builder
- Director: Conductor
- Product: Product Role
3.2 Builder pattern timing diagram
The builder pattern timing diagram is shown below:
04. Builder's Case Practice
4.1 Building a House Case Development
For example, let's consider how to create a House object.
In order to build a simple house, you'll need to build four walls and a floor, install a door, install a pair of windows, and build a roof. But what if you want a bigger, brighter house with a backyard and other amenities (like a heating system, plumbing, and electrical wiring)?
The simplest solution is to extend the base class House and create a set of subclasses to cover all combinations of parameters.
However, you will end up with a fair number of subclasses. Any new parameters, such as porch styles, will require further extensions to this hierarchy.builder patternAllows you to build complex objects step-by-step.
4.2 Building houses in the ordinary way
To build a house using the normal way, the code is shown below:
public class BuilderHouse {
public static void main(String[] args) {
CommonHouse commonHouse = new CommonHouse();
();
HeightBuilding heightBuilding = new HeightBuilding();
();
}
public static abstract class AbstractHouse {
/**
* lay foundations
*/
public abstract void buildBasic();
/**
* build a wall
*/
public abstract void buildWalls();
/**
* fig. put a ceiling (on spending, prize, ambition etc)
*/
public abstract void roofed();
public void build() {
buildBasic();
buildWalls();
roofed();
}
}
public static class CommonHouse extends AbstractHouse {
@Override
public void buildBasic() {
(" 普通房子lay foundations ");
}
@Override
public void buildWalls() {
(" 普通房子build a wall ");
}
@Override
public void roofed() {
(" 普通房子fig. put a ceiling (on spending, prize, ambition etc) ");
}
}
public static class HeightBuilding extends AbstractHouse {
@Override
public void buildBasic() {
(" 高楼lay foundations ");
}
@Override
public void buildWalls() {
(" 高楼房子build a wall ");
}
@Override
public void roofed() {
(" 高楼房子fig. put a ceiling (on spending, prize, ambition etc) ");
}
}
}
analyze
- Pros: better understanding, easy to use
- Disadvantages: The program structure is too simple, there is no design cache layer object, the program is not good for extension and maintenance. This design solution encapsulates the product (i.e., the house) and the process of creating the product (i.e., the process of building the house), which increases coupling
- Improvement: use the builder pattern to decouple the product and the product building process
4.3 Constructor optimization for building a house
Using the builder pattern for house building
private void test() {
///Build a normal house.
//Preparing to create a house conductor
HouseDirector houseDirector = new HouseDirector(new CommonHouse());
//Finish building the house.,Back to Products(Ordinary house)
House commonHouse = ();
("Ordinary house:" + ());
///put up a tall building
//rebuilder,Replace it with a high-rise.
(new HighBuilding());
//Finish building the house.,Back to Products(skyscraper)
House highBuilding = ();
("skyscraper:" + ());
}
/**
* offerings->Product
*/
public class House {
private String basic;
private String wall;
private String roofed;
public String getBasic() {
return basic;
}
public void setBasic(String basic) {
= basic;
}
public String getWall() {
return wall;
}
public void setWall(String wall) {
= wall;
}
public String getRoofed() {
return roofed;
}
public void setRoofed(String roofed) {
= roofed;
}
public House(String basic, String wall, String roofed) {
= basic;
= wall;
= roofed;
}
public House() {
}
@Override
public String toString() {
return "House{" +
"basic='" + basic + '\'' +
", wall='" + wall + '\'' +
", roofed='" + roofed + '\'' +
'}';
}
}
/**
* Abstract builder
*/
public abstract class HouseBuilder {
/**
* combinatorialHouse
*/
protected House house = new House();
//-------------------------Write up the flow of the build--------------------------
/**
* lay foundations
*/
public abstract void buildBasic();
/**
* build a wall
*/
public abstract void buildWalls();
/**
* fig. put a ceiling (on spending, prize, ambition etc)
*/
public abstract void roofed();
/**
* 建造好building (single- or two-story)后将offerings(building (single- or two-story)) come (or go) back
*
* @return
*/
public House buildHouse() {
return house;
}
}
/**
* Specific builders
*/
public class CommonHouse extends HouseBuilder {
@Override
public void buildBasic() {
("Ordinary houselay foundations5meter (classifier) ");
("foundations (of a building)5meter (classifier)");
}
@Override
public void buildWalls() {
("Ordinary housebuild a wall10cm ");
("walls10cm");
}
@Override
public void roofed() {
("Ordinary house屋顶 ");
("Ordinary house屋顶");
}
}
/**
* Specific builders
*/
public class HighBuilding extends HouseBuilder {
@Override
public void buildBasic() {
("skyscraper的lay foundations100meter (classifier) ");
("foundations (of a building)100meter (classifier)");
}
@Override
public void buildWalls() {
("skyscraper的build a wall20cm ");
("walls20cm");
}
@Override
public void roofed() {
("skyscraper的Transparent roofs ");
("Transparent roofs");
}
}
/**
* director,Calling the production method,Back to Products
*/
public class HouseDirector {
/**
* polymerization
*/
HouseBuilder houseBuilder = null;
/**
* Mode 1:Constructor passes in houseBuilder
*
* @param houseBuilder
*/
public HouseDirector(HouseBuilder houseBuilder) {
= houseBuilder;
}
/**
* Mode 2:pass (a bill or inspection etc)setter transmitted inwards houseBuilder
*
* @param houseBuilder
*/
public void setHouseBuilder(HouseBuilder houseBuilder) {
= houseBuilder;
}
/**
* director统一管理建造building (single- or two-story)的流程
*
* @return
*/
public House constructHouse() {
();
();
();
return ();
}
}
05. Builder Model Expansion
5.1 Can builders simplify?
It can be simplified, the simplification of the builder pattern: the
- Omit Abstract Builder Role: Abstract builders can be omitted if only one concrete builder is needed in the system.
- Omitting the Commander Role: In the case where there is only one concrete builder, the Commander Role can also be omitted if the Abstract Builder Role has been omitted, allowing the Builder Role to play the dual role of Commander and Builder.
5.2 Differences from the Factory Pattern
In fact, the factory pattern is used to create different but related types of objects (a set of subclasses inheriting from the same parent class or interface), with a given parameter determining which type of object is created.
The builder pattern is used to create complex objects of one type. Different objects are "customized" by setting different optional parameters.
There is a classic example online that explains the difference well.
A customer walks into a restaurant and orders food, and we utilizefactory model, to make different foods such as pizza, burgers, and salads based on different choices made by the user. For pizza, the user again has various toppings that can be customized, such as cheese, tomato, cheese, and we make pizza based on different toppings chosen by the user through the builder mode.
And don't be so collegiate that you have to putfactory model、builder patternDivided so clearly, what we need to know is why each pattern is so designed and what problems it can solve. Only by understanding these most essential things, we can not be rigid, can be applied flexibly, and even can be mixed with a variety of patterns to create new patterns to solve specific scenarios.
06. Analysis of the advantages and disadvantages of builders
6.1 What are the advantages?
Builder Benefits Analysis
- In the builder pattern, the client does not need to know the details of the internal composition of the product, decoupling the product itself from the product creation process, allowing the same creation process to create different product objects.
- Each concrete builder is relatively independent of the other concrete builders, so it is easy to replace a concrete builder or add a new concrete builder, and users can use different concrete builders to get different product objects.
- The product creation process can be more finely controlled. Breaking down complex product creation steps into different methods makes the creation process clearer and easier to control with a program.
- Add a new concrete builder without modifying the code of the original class library, the commander class for the abstract builder class programming, the system is easy to expand, in line with the "principle of open and closed".
6.2 Points of deficiency analysis
Builder Drawback Analysis
- builder patternThe products created generally have more in common and their components are similar, if the products are very different from each other, they are not suitable for using the builder pattern, so its use is somewhat limited.
- If the product has complex internal changes, this may result in the need to define many concrete builder classes to implement such changes, causing the system to become very large.
07. Constructor Pattern Summary
7.1 Summary of the model
The Builder pattern has several benefits:
- Builder's setter function can include safety checks that can ensure that the build process is safe and efficient.
- Builder's setter functions are named functions, which make sense and are easier to read and maintain as opposed to the constructor's long list of functions.
- Multiple objects can be constructed using a single Builder object, and the parameters of the Builder can be adjusted by using the setter function when creating the object
Of course the Builder pattern has its drawbacks:
- For more code, you need an inner class like Builder
- increases the runtime overhead of class creation, but when a class has many parameters, the benefits of the Builder pattern far outweigh its drawbacks.
7.2 More content recommendations
module (in software) | descriptive | note |
---|---|---|
GitHub | Multiple YC series of open source projects , including Android component libraries , and a number of cases | GitHub |
Blog Roundup | Bringing together Java, Android, C/C++, network protocols, algorithms, programming summaries and more! | YCBlogs |
design pattern | Six Design Principles, 23 Design Patterns, Design Pattern Cases, Object-Oriented Thinking | design pattern |
Java Advanced | Data Design and Principles, Object Oriented Core Ideas, IO, Exceptions, Threading and Concurrency, JVM | Java Advanced |
network protocol | Practical examples of networking, network principles and layering, Https, network requests, troubleshooting | network protocol |
computer theory | Computer Architecture, Frames, Memory, CPU Design, Memory Design, Instruction Programming Principles, Exception Handling Mechanisms, IO Operations and Principles | computer foundation |
Learning C Programming | A systematic and comprehensive tutorial for learning three to four comprehensive cases at the introductory level of the C language. | C programming |
C++ programming | Systematic and comprehensive instructional tutorials for the introductory level of the C++ language, concurrent programming, and core principles. | C++ programming |
algorithmic practice | Columns, arrays, chain tables, stacks, queues, trees, hashes, recursion, lookups, sorting, etc. | Leetcode |
Android | Basic primer, open source library interpretation, performance optimization, Framework, program design | Android |
23 Design Patterns
23 Design Patterns & Description & Core Roles | including through |
---|---|
creation-based model Provides use cases for creating objects. Ability to separate the creation of objects from the use of objects in a software module |
Factory Pattern Abstract Factory Pattern (Abstract Factory Pattern) Singleton Pattern Builder Pattern Prototype Pattern |
structural model Focus on combinations of classes and objects. Describe howCombining classes or objects to form larger structures |
Adapter Pattern (Adapter Pattern) Bridge Pattern Filter Pattern (Filter, Criteria Pattern) Composite Pattern Decorator Pattern Facade Pattern Flyweight Pattern Proxy Pattern |
behavioral model Special attention is paid to communication between objects. The main solution is "interaction between classes or objects". |
Chain of Responsibility Pattern (Chain of Responsibility Pattern) Command Pattern Interpreter Pattern (Interpreter Pattern) Iterator Pattern Mediator Pattern (Mediator Pattern) Memorandum Pattern (Memento Pattern) Observer Pattern (OOP) State Pattern Null Object Pattern (NOP) Strategy Pattern Template Pattern Visitor Pattern |