In system development, there is often a need to create a complex object. This complex object is composed of several child parts. For example, we now need to assemble a computer, which consists of CPU, motherboard, hard disk, memory, display, mouse, keyboard…… And other assembly, and the choice needs to be based on our real use scenarios to assemble different configurations of the computer. For example, colleagues in the same company have different requirements for computer configuration in different positions. The configuration of r & D students may be high, while the configuration of civilian students need not be so high.

Students in the IT operation and maintenance department of the company need to assemble computers with different configurations according to different needs. Flexible replacement of each component, as required assembly. Similar to the creation of a product in this scenario, the Builder pattern described below is a good description of this scenario.

Builder model

Definition of Builder pattern: It refers to the separation of the construction of a complex object from its representation, so that the same construction process can create different representations, such a design pattern is called the Builder pattern. It takes a complex object and breaks it down into simple objects and builds it step by step. It separates change from immutability, that is, the components of a product are constant, but each part can be chosen flexibly. In addition, the Builder pattern is an object creation pattern.

Structure and Implementation

Builder mode is composed of four elements: product, abstract Builder, concrete Builder and commander. Now we analyze its basic structure and implementation method.

  • A Product role: It is a complex object with multiple components that are created by a specific builder.
  • Abstract Builder: This is an interface that contains an abstract method for creating the various child parts of a product, usually including a method getResult() that returns a complex product.
  • Concrete Builder: to realize the Builder interface and complete the Concrete creation method of each part of complex products.
  • Director: It calls the component construction and assembly methods in the builder object to create a complex object. There is no product-specific information in the Director.

The structure diagram is as follows:

Example code: Use the example of assembling a computer introduced at the beginning of this article to complete the builder pattern case;

  • Product role
public class Computer {
	
	private String cpu;
	
	private String disk;
	
	private String ram;

	private String screen;
	
	private String mouse;
	
	private String keyboard;

	public String getCpu(a) {
		return cpu;
	}

	public void setCpu(String cpu) {
		this.cpu = cpu;
	}

	public String getDisk(a) {
		return disk;
	}

	public void setDisk(String disk) {
		this.disk = disk;
	}

	public String getRam(a) {
		return ram;
	}

	public void setRam(String ram) {
		this.ram = ram;
	}

	public String getScreen(a) {
		return screen;
	}

	public void setScreen(String screen) {
		this.screen = screen;
	}

	public String getMouse(a) {
		return mouse;
	}

	public void setMouse(String mouse) {
		this.mouse = mouse;
	}

	public String getKeyboard(a) {
		return keyboard;
	}

	public void setKeyboard(String keyboard) {
		this.keyboard = keyboard;
	}

	@Override
	public String toString(a) {
		return "Computer [cpu=" + cpu + ", disk=" + disk + ", ram=" + ram + ", screen=" + screen + ", mouse=" + mouse
				+ ", keyboard=" + keyboard + "]"; }}Copy the code
  • Abstract builder
public abstract class AbstractBuilder {
	
	/** * Specific product */
   protected Computer computer = new Computer();
	
	/** * Configure the CPU */
	protected abstract void setCpu(a);
	
	/** * Configure disk */
	protected abstract void setDisk(a);
	
	/** * configure ram */
	protected abstract void setRam(a);
	
	/** * configure screen */
	protected abstract void setScreen(a);
	

	/** * Configure the keyboard */
	protected abstract void setKeyboard(a);
	

	/** * Configure mouse */
	protected abstract void setMouse(a);
	
	
	/** * returns create concrete object *@return* /
	public Computer getResult(a) {
		returncomputer; }}Copy the code
  • Specific builder 1
public class ConcreteBuilderApple extends AbstractBuilder{

	@Override
	protected void setCpu(a) {
		computer.setCpu("M1");
	}

	@Override
	protected void setDisk(a) {
		computer.setDisk("Seagate 2TB");
	}

	@Override
	protected void setRam(a) {
		computer.setRam("Kingston 16G");
	}

	@Override
	protected void setScreen(a) {
		computer.setScreen("Sasung");
	}

	@Override
	protected void setKeyboard(a) {
		computer.setKeyboard("apple keyboard");
	}

	@Override
	protected void setMouse(a) {
		computer.setMouse("apple mouse"); }}Copy the code
  • Specific Builder 2
public class ConcreteBuilderHuawei extends AbstractBuilder{

	protected void setCpu(a) {
		computer.setCpu("Intel");
	}

	protected void setDisk(a) {
		computer.setDisk("Sasung 1TB");
	}

	protected void setRam(a) {
		computer.setRam("AMD 8G");		
	}

	
	protected void setScreen(a) {	
		computer.setScreen("Sony");
	}

	
	protected void setKeyboard(a) {
		computer.setKeyboard("Huawei");		
	}
	
	protected void setMouse(a) {
		computer.setKeyboard("Logitech"); }}Copy the code
  • commander
public class ComputerDirector {
	
	AbstractBuilder builder = null;
	
	public ComputerDirector(AbstractBuilder builder){
		this.builder = builder;
	}
	
    // Create an instance
	public Computer builderComputer(a) {
		builder.setCpu();
		builder.setDisk();
		builder.setRam();
		builder.setRam();
		builder.setMouse();
		builder.setKeyboard();
		builder.setScreen();
		returnbuilder.getResult(); }}Copy the code
  • Using the
public class Client {

	public static void main(String[] args) {
		ConcreteBuilderApple concreteBuilderApple =  new ConcreteBuilderApple();
		// Suppose we produce Apple computers
		ComputerDirector computerDirector = newComputerDirector(concreteBuilderApple); Computer macBookPro =computerDirector.builderComputer(); System.out.println(macBookPro.toString()); }}Copy the code

Execution Result:

Computer [cpu=M1, disk=Seagate 2TB, ram=Kingston 16G, screen=Sasung, mouse=apple mouse, keyboard=apple keyboard]
Copy the code

Case Structure Diagram:

Usage scenarios

When the product to be created has a complex creation process, the common creation process can be extracted, and then the specific implementation class can customize the creation process, so that the same creation behavior can produce different products, separation of creation and presentation, so that the flexibility of product creation is greatly increased.

The Builder pattern applies to the following scenarios:

  • Same method, different order of execution, produce different results.
  • Multiple parts, or parts, can be assembled into an object but produce different results.
  • The product class is very complex, or different calls in the product class have different effects.
  • Initializing an object is extremely complex with many parameters, many of which have default values.

The static class approach is often used in projects to implement the builder

Also use our example above to demonstrate the need to change the product class;

public class Computer2 {
	
	private String cpu;
	
	private String disk;
	
	private String ram;

	private String screen;
	
	private String mouse;
	
	private String keyboard;

	public String getCpu(a) {
		return cpu;
	}

	public void setCpu(String cpu) {
		this.cpu = cpu;
	}

	public String getDisk(a) {
		return disk;
	}

	public void setDisk(String disk) {
		this.disk = disk;
	}

	public String getRam(a) {
		return ram;
	}

	public void setRam(String ram) {
		this.ram = ram;
	}

	public String getScreen(a) {
		return screen;
	}

	public void setScreen(String screen) {
		this.screen = screen;
	}

	public String getMouse(a) {
		return mouse;
	}

	public void setMouse(String mouse) {
		this.mouse = mouse;
	}

	public String getKeyboard(a) {
		return keyboard;
	}

	public void setKeyboard(String keyboard) {
		this.keyboard = keyboard;
	}

	@Override
	public String toString(a) {
		return "Computer [cpu=" + cpu + ", disk=" + disk + ", ram=" + ram + ", screen=" + screen + ", mouse=" + mouse
				+ ", keyboard=" + keyboard + "]";
	}
	
	public static class Builder{
		Computer2 product = new Computer2();
		
		public Builder setCpu(String cpu) {
			product.cpu = cpu;
			return this;
		}
		
		public Builder setRam(String ram) {
			product.ram = ram;
			return this;
		}
		
		public Builder setDisk(String disk) {
			product.disk = disk;
			return this;
		}
		
		
		public Builder setScreen(String screen) {
			product.screen = screen;
			return this;
		}
		
		public Builder setMouse(String mouse) {
			product.mouse = mouse;
			return this;
		}
		
		public Builder setKeyboard(String keyboard) {
			product.keyboard = keyboard;
			return this;
		}
		
		
	     public Computer2 getResult(a) {
		     returnproduct; }}}Copy the code
  • To use:
public class Client2 {
	
	public static void main(String[] args) {	
	  Computer2 computer2 =	new Builder().setCpu("Intel").setDisk("wd").setRam("AMD").setScreen("SONY").getResult(); System.out.println(computer2); }}Copy the code

The execution result

Computer [CPU =Intel, Disk =wd, RAM =AMD, screen= SONY, mouse=null, keyboard=null]
Copy the code

Simplify the use of the builder pattern by constructing concrete products with static inner classes (commander, builder). Makes the code more brief, the disadvantage is the violation of the open closed principle, product properties change, inner class also have to change.

The advantages and disadvantages

Advantages:

  • Good encapsulation, complex object construction process and presentation separate
  • Good expansibility, each specific builder is independent of each other, which is conducive to the decoupling of the system.
  • The client does not have to know the details of the internal composition of the product, and the builder can gradually refine the creation process without any impact on other modules, facilitating the control of detail risk.

Disadvantages:

  • The components of the product must be the same, which limits its range of use.
  • If the internal change of the product is complex, if the internal change of the product occurs, the builder also needs to synchronize the modification, and the later maintenance cost is large.

Use of the Builder pattern in source code

SqlSessionFactoryBuiler class in MyBatis uses the builder mode. SqlSessionFactory is generated by the specific SqlSessionFactoryBuilder. Let’s look at the source code section

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}
Copy the code

DefaultSqlSessionFactory creates the Configuration object, and SqlSessionFactoryBuilder acts as our director.

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                inputStream.close();
            } catch (IOException var13) {
            }
        }
        return var5;
    }
Copy the code

Configuration object is a complex object. The builder of the Configuration object is XMLConfigBuilder and the builder of the Configuration object is BaseBuilder. Construct the Configuration item, and the parse () method gets the Configuration object we constructed.

// The construction process
    private void parseConfiguration(XNode root) {
        try {
            Properties settings = this.settingsAsPropertiess(root.evalNode("settings"));
            this.propertiesElement(root.evalNode("properties"));
            this.loadCustomVfs(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectionFactoryElement(root.evalNode("reflectionFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: "+ var3, var3); }}// Get the result
public Configuration parse(a) {
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            return this.configuration; }}Copy the code