preface
As we know, common design patterns fall into three broad categories: creative, behavioral, and structural. Today we are going to talk about the Builder mode in the Creation mode, its usage scenarios, advantages and disadvantages, components, practical examples, and its use in the JDK.
We are familiar with the factory pattern, which is mainly used to extract instantiated objects and put them into a class for unified maintenance and management, so as to achieve decoupling and improve project scalability and maintainability. Builder mode is similar in functionality to Factory mode, but with a different focus. Let’s take a closer look at the creator pattern.
An introduction to the Builder pattern
Builder Pattern, also translated as Builder Pattern, is also informally called generator Pattern. The official definition is to separate the construction of a complex object from its representation, so that the same construction process can create different representations.
In plain English, a complex object is broken down into simple objects and then built into a product step by step. It is mainly to separate the changing part from the invariable part, that is, the components of the product are invariable, but each part can be flexibly selected.
Just like we want to assemble a computer, need CPU, motherboard, memory, hard disk, graphics card, chassis, display, keyboard, mouse and other parts, these parts are unchanged, but according to their own economic situation and the use of different scenarios, can choose different configurations of each part.
Building a complex object like this often requires multiple steps to complete. And the hardware configuration information used at each step may differ. Also, not every computer needs a monitor or mouse. Before we demonstrate the above scenario in code, let’s look at some of the roles and functions that the Builder pattern typically involves.
Structure and Function
The Builder pattern usually consists of four roles: product, Abstract Builder, Concrete builder, and director.
Each role has the following functions:
- Product Role: A specific Product object to be created, usually a complex object with multiple components.
- Abstract Builder: An interface or abstract class for creating product objects, which contains abstract methods for creating the various child parts of the product and methods for returning the final object.
- Concrete Builder: To realize the Builder interface and complete the construction and assembly of different complex products.
- Director: Build an object that uses the Builder interface to create complex objects. It is responsible for controlling the production process of product objects, while isolating the production process of users and objects. No specific product information is involved in the commander.
The structure is shown in the figure:
Implementation of patterns
When we use laptops as developers, we also tend to have an external keyboard, mouse, and external monitor. At this point, a set of computer equipment includes: notebook computer, mouse, keyboard, monitor. Of course, depending on individual needs, some people don’t need a mouse, some people don’t need an external keyboard, and some people don’t need an external monitor, which constitutes a different product.
Let’s first define the product class Computer, which is the overall configuration of the development Computer:
Public class Computer {/** * private Laptop */ private Laptop; /** * private Mouse Mouse; /** * private Screen Screen; /** * private Keyboard */ private Keyboard; Public void show() {system.out.println (" laptop configuration: "+ laptop.getName()); System.out.println(" Mouse configuration: "+ mouse.getName()); System.out.println(" Display configuration: "+ screen.getName()); System.out.println(" Keyboard configuration: "+ keyboard.getName()); } // omit getter/setter methods}Copy the code
The product consists of four components: notebook computer, mouse, monitor and keyboard.
Abstract Builder: Contains an abstract method for creating the child parts of a product. Product construction steps that are common across all type generators are declared through interfaces.
Public Interface ComputerBuilder {** * build laptop */ void constructLaptop(); /** * constructMouse */ void constructMouse(); /** * constructScreen */ void constructScreen(); /** * constructKeyboard */ void constructKeyboard(); /** * returns the final product object */ Computer getResult(); }Copy the code
Abstract builders provide a unified approach to building each component, and implementors are responsible for the various changes that may occur within each component.
Concrete Builder: Implements the abstract Builder interface. In the current business scenario, we assume that there are two types of computer configurations provided by the company, one is ordinary configuration and the other is high-end configuration. This creates two concrete builders that provide different implementations of the construction process.
Common configuration implementation:
public class CommonComputerBuilder implements ComputerBuilder { private Computer computer = new Computer(); @override public void constructLaptop() {constructLaptop = new Laptop("A"," huawei Laptop "); computer.setLaptop(laptop); } @override public void constructMouse() {Mouse Mouse = new Mouse("A"," Mouse "); computer.setMouse(mouse); } @override public void constructScreen() {constructScreen = new Screen("A"," c "); computer.setScreen(screen); } @override public void constructKeyboard() {Keyboard = new Keyboard("A"," normal Keyboard "); computer.setKeyboard(keyboard); } @Override public Computer getResult() { return computer; }}Copy the code
Advanced configuration implementation:
public class SupperComputerBuilder implements ComputerBuilder { private Computer computer = new Computer(); @Override public void constructLaptop() { Laptop laptop = new Laptop("S", "Mac Boor Pro"); computer.setLaptop(laptop); } @override public void constructMouse() {Mouse Mouse = new Mouse("A", "Mouse "); computer.setMouse(mouse); } @override public void constructScreen() {constructScreen = new Screen("S", "c "); computer.setScreen(screen); } @override public void constructKeyboard() {Keyboard = new Keyboard("S", "machine Keyboard "); computer.setKeyboard(keyboard); } @Override public Computer getResult() { return computer; }}Copy the code
In this concrete builder, the configuration information is set for each component, which is simplified so that each component only has a type and name. In practice, each component object may contain different data items.
Director: Calls methods in builder to complete the creation of complex objects.
public class Director { private ComputerBuilder builder; public Director(ComputerBuilder builder) { this.builder = builder; } public Computer construct() {builder.constructlaptop (); builder.constructMouse(); builder.constructScreen(); builder.constructKeyboard(); return builder.getResult(); }}Copy the code
The director encapsulates the entire production process and isolates the client, who only needs to get the final product, not the assembly process.
Examples of client calls:
Public class Client {public static void main(String[] args) {// ComputerBuilder builder = new CommonComputerBuilder(); Director director = new Director(builder); Computer product = director.construct(); product.show(); System.out.println("------------------"); // Build computer Builder = new SupperComputerBuilder(); director = new Director(builder); product = director.construct(); product.show(); // Can be extended to other configurations of the build implementation}}Copy the code
The execution program prints the following results:
Laptop configuration: Huawei laptop mouse configuration: Wireless mouse monitor configuration: LCD keyboard configuration: common keyboard ------------------ Laptop configuration: Mac Boor Pro mouse configuration: wireless mouse monitor configuration: LCD curved keyboard configuration: mechanical keyboardCopy the code
For the example above, we can find that if we need another configuration of the computer, we just need to create a Builder implementation class, and the instruction class assembly order can also be adjusted. In this case, the original function will not be affected, in line with the design mode of the open and closed principle.
Application scenarios
In fact, the most important thing is to understand the design pattern. Only when you know the applicable scenario can you use the design pattern accurately.
As you can see from the examples above, the Builder pattern creates complex objects that are suitable for scenarios where specific parts of a product often change, but the algorithms that put them together are relatively stable.
The Builder pattern applies to the following scenarios:
- The object created is complex and consists of multiple parts, each of which faces complex changes, but the building sequence between the components is stable and the results are not the same.
- The product class is very complex, or different calls in the product class have different effects;
- The various forms of products to be created, with similar manufacturing processes and only detailed differences;
- Using generators to construct composite trees or other complex objects, the builder pattern can be used to construct products step by step and delay.
- There are N optional arguments in the constructor, so the new instances are cumbersome. You need to reload the constructor multiple times, and many of the arguments have default values.
Advantages and Disadvantages
The advantages and disadvantages of the Builder pattern should be understood.
Advantages:
- Each specific builder is independent of each other and can be easily replaced or new specific builder can be added to facilitate the expansion of the system.
- Good encapsulation, construction and presentation separation, the client does not have to know the details of the internal composition of the product, easy to control the risk of detail.
- The product creation process can be more finely controlled, and the builder can refine the creation process step by step without any impact on other modules.
Disadvantages of the Builder model:
- Need to create additional Builder interfaces and implementation classes;
- If the internal changes of the product are complex, many specific builder classes may need to be defined to implement the changes, resulting in a large system.
- If the product internal changes, the builder also need to synchronize modification, later maintenance costs are large.
- The products created by the Builder mode generally have more in common and their components are similar. If the differences between the products are large, the builder mode is not suitable for use, so its application scope is limited to a certain extent.
The Builder pattern can be changed as needed during the application process. If only one type of product is being created and only one concrete Builder is needed, the abstract Builder can be omitted, or even the director role can be omitted.
Factory vs. Builder mode
The Builder pattern and the factory pattern have different concerns: the Builder pattern focuses on the assembly process of parts, while the factory method pattern focuses on the creation process of parts, and the two can be used together.
The differences between builder mode and factory mode are as follows: (1) Builder mode pays more attention to method call order, while factory mode pays more attention to object creation; (2) Different forces are used to create objects. The Builder mode creates complex objects with various complex components, while the factory mode creates the same objects. (3) The focus is different. The factory mode only needs to create the object, while the builder mode not only needs to create the object, but also needs to know what parts the object is composed of; (4) Builder mode According to the different order in the construction process, the final object component composition is also different.
Builder application in JDK
Our most commonly used StringBuilder class has the most direct application of the Builder pattern. Take a look at the core code:
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
@Override
public StringBuilder append(char c) {
super.append(c);
return this;
}
Copy the code
For details on StringBuilder interface inheritance, see the source code.
summary
This article takes you through the Builder pattern, detailing its composition and the functionality of each part. The most important thing about design patterns is to understand their application scenarios, to use the right design patterns for the right scenarios, and to be flexible with the specific environment.