Builder – Controlling Object Creation

Jul 9, 2020 | , , , , | Programming | 0 comments

The Builder Design Pattern is another one of the GOF creational design patterns. This pattern can be used to simplify the way that complex objects are created in your code resulting in clean code that is much easier to understand and maintain. When implemented correctly a “Builder” is used to construct a complex object following the rules that you have set up in your implementation of the pattern. This pattern is used fairly pervasively, an example would be the WebClient from the Spring Reactive Project which uses a builder for its construction.

Pattern Overview

The Builder pattern is similar to the Factory Pattern in that it is a creational pattern but the Builder pattern is different in the sense that it should be used when you want to simplify constructor calls and maintain tighter control over the way the object is actually constructed. For example, if there is a certain order in which things should be built. You would not be able to achieve this kind of control if you used the Factory pattern on its own.

UML

The following is the generic UML for the builder pattern.

Here we see 4 key components of the pattern which are described as follows:

Product – This is what is actually being built. It is a simple class that contains properties and setters for those properties.

Builder – This is an interface that contains the contract for implementing concrete builders.

ConcreteBuilder – This is a class that implements the Builder interface and contains the functionality for setting the properties on the Product specific to the concrete builder that is being used.

Director – The director takes any class that has implemented the Builder interface and orchestrates the creation of the Product by calling the methods implemented in the ConcreteBuilder respectively.

Example

This all sounds great in the abstract but sometimes I find it difficult to really understand design patterns until there is an actual example that is discussed. In this case, we’ll be discussing the use of this pattern to build Car Objects. In this example, each car has a type of transmission, type of suspension, an exterior, an interior, a seat count, and a wheel type. In this example, we also have two different kinds of cars, a Sports Car and a Family Car each having slightly different values for the properties just mentioned (i.e. a Family car has an automatic transmission whereas a Sports car has a manual transmission). It is also important in this example that the Car is built in a particular order starting with the Transmission and Suspension followed by then building the Exterior and Interior. Then finally the number of seats is specified along with the types of wheels.

UML

Here is the UML diagram for this example, each class is described below.

In this diagram we see the following:

Car – This is the product of the builder pattern, the class that is going to be built.

CarBuilder – This is the Interface that is used to specify what methods a builder needs to implement in order to conform to the contract that is required for building a Car.

SportsCarBuilder / FamilyCarBuilder – These are the concrete builders that implement the CarBuilder interface and contain the details for setting the properties for each type of car respectively.

AutomotiveEngineer – This is the Director, the class that orchestrates the creation of Car objects without knowledge of the specific type of Builder being used.

Code

Now we will look at this UML in Java to get a more concrete idea of what this could look like.

Car.java

package solutions.reformit.blog.builderpattern;
public class Car {
	private String transmission;
	private String suspension;
	private String exterior;
	private String interior;
	private Integer seatCount;
	private String wheelType;
	public void setTransmission(String transmission) {
		this.transmission = transmission;
	}
	public void setSuspension(String suspension) {
		this.suspension = suspension;
	}
	public void setExterior(String exterior) {
		this.exterior = exterior;
	}
	public void setInterior(String interior) {
		this.interior = interior;
	}
	public void setSeatCount(Integer seatCount) {
		this.seatCount = seatCount;
	}
	public void setWheelType(String wheelType) {
		this.wheelType = wheelType;
	}
	public String toString() {
        return "Tranmission = {" + transmission +
        		"} \n Suspension = {" + suspension +
        		"} \n Exterior = {" + exterior +
        		"} \n Interior = {" + interior +
        		"} \n Seat Count = {" + seatCount.toString() +
        		"} \n Wheel Type = {" + wheelType + "}";
	}
}

Here we see a basic Java object with properties that define a car and setters to populate those values. We also see a method for printing a String of what this car’s property values are. These values will differ depending on the type of Builder used to create this Car and return it as an object.

CarBuilder.java

package solutions.reformit.blog.builderpattern;
public interface CarBuilder {
	void buildTransmission();
	void buildSuspension();
	void buildExterior();
	void buildInterior();
	void buildSeatCount();
	void buildWheelType();
	Car getCar();
}

Here we see the CarBuilder Interface. In this interface there is a contract specified that each Builder that plans on building Car objects must conform to. Each concrete builder will provide it’s own implementation of each of these methods based on the type of Car that is being built.

SportsCarBuilder.java

package solutions.reformit.blog.builderpattern;
public class SportsCarBuilder implements CarBuilder {
	private Car car;
	public SportsCarBuilder() {
		this.car = new Car();
	}
	@Override
	public void buildTransmission() {
		car.setTransmission("Manual");
		System.out.println("SportsCarBuilder: Tranmission complete...");
	}
	@Override
	public void buildSuspension() {
		car.setSuspension("Racing Shocks, Re-inforced Springs");
		System.out.println("SportsCarBuilder: Suspension complete...");
	}
	@Override
	public void buildExterior() {
		car.setExterior("Aerodynamic");
		System.out.println("SportsCarBuilder: Exterior complete...");
	}
	@Override
	public void buildInterior() {
		car.setInterior("Leather Seats, Tacomoter");
		System.out.println("SportsCarBuilder: Interior complete...");
	}
	@Override
	public void buildSeatCount() {
		car.setSeatCount(2);
		System.out.println("SportsCarBuilder: Seat Count complete...");
	}
	@Override
	public void buildWheelType() {
		car.setWheelType("Low Profile, Racing");
		System.out.println("SportsCarBuilder: Wheel Type complete...");
	}
	@Override
	public Car getCar() {
		System.out.println("SportsCarBuilder: Sports Car complete...");
		return this.car;
	}
}

Here we see the concrete implementation of the CarBuilder interface for Sports Cars. Each build method contains the details specific to what a Sports Car might have.

FamilyCarBuilder.java

package solutions.reformit.blog.builderpattern;
public class FamilyCarBuilder implements CarBuilder {
	private Car car;
	public FamilyCarBuilder() {
		this.car = new Car();
	}
	@Override
	public void buildTransmission() {
		car.setTransmission("Automatic");
		System.out.println("FamilyCarBuilder: Tranmission complete...");
	}
	@Override
	public void buildSuspension() {
		car.setSuspension("Road Shocks and Springs");
		System.out.println("FamilyCarBuilder: Suspension complete...");
	}
	@Override
	public void buildExterior() {
		car.setExterior("Sedan");
		System.out.println("FamilyCarBuilder: Exterior complete...");
	}
	@Override
	public void buildInterior() {
		car.setInterior("Stain Resistent Upholstery");
		System.out.println("FamilyCarBuilder: Interior complete...");
	}
	@Override
	public void buildSeatCount() {
		car.setSeatCount(4);
		System.out.println("FamilyCarBuilder: Seat Count complete...");
	}
	@Override
	public void buildWheelType() {
		car.setWheelType("Extended Mileage");
		System.out.println("FamilyCarBuilder: Wheel Type complete...");
	}
	@Override
	public Car getCar() {
		System.out.println("FamilyCarBuilder: Family Car complete...");
		return this.car;
	}
}

Here we see the concrete implementation of the CarBuilder interface for Family Cars. Each build method contains the details specific to what a Family Car might have for example a Sports Car has a manual transmission but the Family Car has an automatic transmission.

AutomotiveEngineer.java

package solutions.reformit.blog.builderpattern;
public class AutomotiveEngineer {
	private CarBuilder carBuilder;
	public AutomotiveEngineer(CarBuilder carBuilder) {
		this.carBuilder = carBuilder;
	}
	public Car build() {
		this.carBuilder.buildTransmission();
		this.carBuilder.buildSuspension();
		this.carBuilder.buildExterior();
		this.carBuilder.buildInterior();
		this.carBuilder.buildSeatCount();
		this.carBuilder.buildWheelType();
		return this.carBuilder.getCar();
	}
}

Now we see the implementation of the Builder Pattern Director Class which in our case is the AutomotiveEngineer. The important thing to notice here is that this class receives a CarBuilder and without knowing which type of builder it constructs the Car Object using the various build methods based on the CarBuilder interface. It also has control over the order in which these build methods are called ensuring that the transmission and suspension are built first, followed by the Exterior and Interior and then finally it builds the Seat Count and Wheel Type last after the rest o the object has already been specified.

Output

I have used JUnit as a quick way to run this example. The Test Class that I have build is coded as follows and contains an example of creating a Sports car as well as a Family car.

AutomotiveEngineerTest.java

package solutions.reformit.blog.builderpattern;
import org.junit.jupiter.api.Test;
class AutomotiveEngineerTest {
	@Test
	void test() {
		CarBuilder sportsCarBuilder = new SportsCarBuilder();
		AutomotiveEngineer sportsCarEngineer = new AutomotiveEngineer(sportsCarBuilder);
		Car sportsCar = sportsCarEngineer.build();
		System.out.println("[Sports Car] " + sportsCar);
		System.out.println();
		CarBuilder familyCarBuilder = new FamilyCarBuilder();
		AutomotiveEngineer familyCarEngineer = new AutomotiveEngineer(familyCarBuilder);
		Car famlyCar = familyCarEngineer.build();
		System.out.println("[Family Car] " + famlyCar);
		System.out.println();
	}
}

When this test case is executed the following is outputted to the console:

Here you can see that the Sports Car object has properties set based on the values specified by the SportsCarBuilder whereas the Family Car object has properties set based on the values specified by the FamilyCarBuilder. You can also see the step by step construction of each variation of the Car object by the CarBuilder implementations based on the implementation of the AutomotiveEngineer class, our Director.

Conclusion

This is admittedly a simple example but the goal here is to begin to understand how this pattern works so that you can realize how it could be used as you move forward in designing solutions. Design Patterns area key tool in the Software Engineer’s toolbox. The Builder pattern is used quite pervasively throughout the industry, particularly in frameworks such as the Spring Framework. It is a pattern that helps abstract away the complexity of object creation in client code, particularly when you need to ensure an object is built in a particular order. If you would like to play with the code it can be found in the following Git Hub repo.

If your company is looking to build software and you need help doing so, software development is our bread and butter. Please feel free to request a free quote to speak with us further about how we can help you successfully build the software that your company needs.

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *