© Vaskaran Sarcar 2019
Vaskaran SarcarJava Design Patternshttps://doi.org/10.1007/978-1-4842-4078-6_3

3. Builder Pattern

Vaskaran Sarcar1 
(1)
Bangalore, Karnataka, India
 

This chapter covers the builder pattern.

GoF Definition

Separate the construction of a complex object from its representation so that the same construction processes can create different representations.

Concept

The builder pattern is useful for creating complex objects that have multiple parts. The creational mechanism of an object should be independent of these parts. The construction process does not care about how these parts are assembled. The same construction process must allow us to create different representations of the objects.

The structure in Figure 3-1 is an example of the builder pattern. The structure is adopted from the Gang of Four book, Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1995).
../images/395506_2_En_3_Chapter/395506_2_En_3_Fig1_HTML.jpg
Figure 3-1

An example of the builder pattern

Product is the complex object that you want to create. ConcreteBuilder constructs and assembles the parts of a product by implementing an abstract interface, Builder. The ConcreteBuilder objects build the product’s internal representations and define the creational process/assembling mechanisms. Builders can also provide methods to get an object that is created and available for use (notice the getVehicle() method in the Builder interface in the following implementation). Director is responsible for creating the final object using the Builder interface. In other words, Director uses Builder and controls the steps/sequence to build the final Product. Builders can also keep reference of the products that they built, so that they can be used again.

Real-World Example

To complete an order for a computer, different parts are assembled based on customer preferences (e.g., one customer can opt for a 500 GB hard disk with an Intel processor, and another customer can choose a 250 GB hard disk with an AMD processor). Consider another example. Suppose that you intend to go on a tour with a travel company that provides various packages for the same tour (for example, they can provide special arrangements, a different kind of vehicle for the sightseeing, etc.). You can choose your package based on your budget.

Computer-World Example

The builder pattern can be used when we want to convert one text format to another text format (e.g., RTF to ASCII text).

Note

The Java.util.Calendar.Builder class is an example in this category, but it is available in Java 8 and onward only. The java.lang.StringBuilder class is a close example in this context. But you need to remember that the GoF definition says that this pattern allows you to use the same construction process to make different representations. In this context, this example does not fully qualify for this pattern.

Illustration

In this example, we have the following participants: Builder, Car, MotorCycle, Product, and Director. The first three are very straightforward; Car and MotorCycle are concrete classes and they implement the Builder interface. Builder is used to create parts of the Product object, where Product represents the complex object under construction.

Since Car and MotorCycle are the concrete implementations of the Builder interface, they need to implement the methods that are declared in the Builder interface. That’s why they needed to supply the body for the startUpOperations() , buildBody() , insertWheels() , addHeadlights() , endOperations() , and getVehicle()methods . The first five methods are straightforward; they are used to perform an operation at the beginning (or end), build the body of the vehicle, insert the wheels, and add headlights. getVehicle() returns the final product. In this case, Director is responsible for constructing the final representation of these products using the Builder interface. (See the structure defined by the GoF). Notice that Director is calling the same construct() method to create different types of vehicles.

Now go through the code to see how different parts are assembled for this pattern.

Class Diagram

Figure 3-2 shows the class diagram of the builder pattern.
../images/395506_2_En_3_Chapter/395506_2_En_3_Fig2_HTML.jpg
Figure 3-2

Class diagram

Package Explorer View

Figure 3-3 shows the high-level structure of the program.
../images/395506_2_En_3_Chapter/395506_2_En_3_Fig3_HTML.jpg
Figure 3-3

Package Explorer view

Implementation

Here’s the implementation.
package jdp2e.builder.demo;
import java.util.LinkedList;
//The common interface
interface Builder
{
     void startUpOperations();
     void buildBody();
     void insertWheels();
     void addHeadlights();
     void  endOperations();
     /*The following method is used to retrieve the object that is constructed.*/
     Product getVehicle();
}
//Car class
class Car implements Builder
{
    private String brandName;
    private Product product;
    public Car(String brand)
    {
        product = new Product();
        this.brandName = brand;
    }
    public void startUpOperations()
    {
      //Starting with brand name
        product.add(String.format("Car model is :%s",this.brandName));
    }
    public void buildBody()
    {
        product.add("This is a body of a Car");
    }
    public void insertWheels()
    {
        product.add("4 wheels are added");
    }
    public void addHeadlights()
    {
        product.add("2 Headlights are added");
    }
    public void endOperations()
    {   //Nothing in this case
    }
    public Product getVehicle()
    {
        return product;
    }
}
//Motorcycle class
class MotorCycle implements Builder
{
    private String brandName;
    private Product product;
    public MotorCycle(String brand)
    {
        product = new Product();
        this.brandName = brand;
    }
    public void startUpOperations()
    {   //Nothing in this case
    }
    public  void buildBody()
    {
        product.add("This is a body of a Motorcycle");
    }
    public void insertWheels()
    {
        product.add("2 wheels are added");
    }
    public void addHeadlights()
    {
        product.add("1 Headlights are added");
    }
    public void endOperations()
    {
        //Finishing up with brand name
        product.add(String.format("Motorcycle model is :%s", this.brandName));
    }
    public Product getVehicle()
    {
        return product;
    }
}
// Product class
class Product
{
   /* You can use any data structure that you prefer.
       I have used LinkedList<String> in this case.*/
    private LinkedList<String> parts;
    public Product()
    {
        parts = new LinkedList<String>();
    }
    public void add(String part)
    {
       //Adding parts
        parts.addLast(part);
    }
    public void showProduct()
    {
      System.out.println(" Product completed as below :");
        for (String part: parts)
        System.out.println(part);
    }
}
// Director class
class Director
{
    Builder builder;
    // Director knows how to use the builder and the sequence of steps.
    public void construct(Builder builder)
    {
        this.builder = builder;
        builder.startUpOperations();
        builder.buildBody();
        builder.insertWheels();
        builder.addHeadlights();
        builder.endOperations();
    }
}
public class BuilderPatternExample {
      public static void main(String[] args) {
             System.out.println("***Builder Pattern Demo***");
        Director director = new Director();
        Builder fordCar = new Car("Ford");
        Builder hondaMotorycle = new MotorCycle("Honda");
        // Making Car
        director.construct(fordCar);
        Product p1 = fordCar.getVehicle();
        p1.showProduct();
        //Making MotorCycle
        director.construct(hondaMotorycle );
        Product p2 = hondaMotorycle.getVehicle();
        p2.showProduct();
      }
}

Output

Here’s the output.
***Builder Pattern Demo***
Product completed as below :
Car model is :Ford
This is a body of a Car
4 wheels are added
2 Headlights are added
Product completed as below :
This is a body of a Motorcycle
2 wheels are added
1 Headlights are added
Motorcycle model is :Honda

Q&A Session

  1. 1.
    What are the advantages of using a builder pattern?
    • You can create a complex object, step by step, and vary the steps. You promote encapsulation by hiding the details of the complex construction process. The director can retrieve the final product from the builder when the whole construction is over. In general, at a high level, you seem to have only one method that makes the complete product. Other internal methods only involve partial creation. So, you have finer control over the construction process.

    • Using this pattern, the same construction process can produce different products.

    • Since you can vary the construction steps, you can vary product’s internal representation.

     
  2. 2.
    What are the drawbacks/pitfalls associated with the builder pattern?
    • It is not suitable if you want to deal with mutable objects (which can be modified later).

    • You may need to duplicate some portion of the code. These duplications may have significant impact in some context and turn into an antipattern.

    • A concrete builder is dedicated to a particular type of product. So, to create different type of products, you may need to come up with different concrete builders.

    • The approach makes more sense if the structure is very complex.

     
  3. 3.

    Can I use an abstract class instead of the interface in the illustration of this pattern?

    Yes. You can use an abstract class instead of an interface in this example.

     
  4. 4.

    How do I decide whether I should use an abstract class or an interface in an application?

    I believe that if you want to have some centralized or default behavior, the abstract class is a better choice. In those cases, you can provide some default implementation. On the other hand, interface implementation starts from scratch. They indicate some kind of rules/contracts on what is to be done (e.g., you must implement the method) but they will not enforce the how part of it. Also, interfaces are preferred when you are trying to implement the concept of multiple inheritance.

    But at the same time, if you need to add a new method in an interface, then you need to track down all the implementations of that interface and you need to put the concrete implementation for that method in all of those places. You can add a new method in an abstract class with a default implementation and the existing code can run smoothly.

    Java has taken special care with this last point. Java 8 introduced the use of ‘default’ keyword in the interface. You can prefix the default word before the intended method signature and provide a default implementation. Interface methods are public by default, so you do not need to mark it with the keyword public.

    These summarized suggestions are from the Oracle Java documentation at https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html .

    You should prefer the abstract class in the following scenarios:
    • You want to share code among multiple closely related classes.

    • The classes that extend the abstract class can have many common methods or fields, or they require non-public access modifiers inside them.

    • You want to use non-static or/and non-final fields, which enables us to define methods that can access and modify the state of the object to which they belong.

    On the other hand, you should prefer interfaces for these scenarios:
    • You expect that several unrelated classes are going to implement your interface (e.g., comparable interface can be implemented by many unrelated classes).

    • You specify the behavior of a particular data type, but it does not matter how the implementer implements that.

    • You want to use the concept of multiple inheritance in your application.

     

Note

In my book Interactive Object-Oriented Programming in Java (Apress, 2016), I discussed abstract classes, interfaces, and the use of the “default” keyword with various examples and outputs. Refer to that book for a detailed discussion and analysis.

  1. 5.

    I am seeing that in cars, model names are added in the beginning, but for motorcycles, model names are added at the end. Is it intentional?

    Yes. It was used to demonstrate the fact that each of the concrete builders can decide how they want to produce the final products. They have this freedom.

     
  2. 6.

    Why are you using a separate class for director? You could use the client code to play the role of the director.

    No one restricts you to do that. In the preceding implementation, I wanted to separate this role from the client code in the implementation. But in the upcoming/modified implementation, I have used the client as a director.

     
  3. 7.

    What do you mean by client code ?

    The class that contains the main() method is the client code. In most parts of the book, client code means the same.

     
  4. 8.

    You mentioned varying steps several times. Can you demonstrate an implementation where the final product is created with different variations and steps?

    Good catch. You asked for a demonstration of the real power of the builder pattern. So, let us consider another example.

     

Modified Illustration

Here are the key characteristics of the modified implementation.
  • In this modified implementation, I consider only cars as the final products.

  • I create custom cars that have the following attributes: a start-up message (startUpMessage), a process completion message (endOperationsMessage), the body material of the car (bodyType), the number of wheels on the car (noOfWheels), and the number of headlights (noOfHeadLights) on the car.

  • The client code is playing the role of a director in this implementation.

  • I have renamed the builder interface as ModifiedBuilder. Apart from the constructCar() and getConstructedCar() methods , each of the methods in the interface has the ModifiedBuilder return type, which helps us to apply method chaining mechanism in the client code.

Modified Package Explorer View

Figure 3-4 shows the modified Package Explorer view.
../images/395506_2_En_3_Chapter/395506_2_En_3_Fig4_HTML.jpg
Figure 3-4

Modified Package Explorer view

Modified Implementation

Here is the modified implementation.
package jdp2e.builder.pattern;
//The common interface
interface ModifiedBuilder
{
       /*All these methods return type is ModifiedBuilder.
        * This will help us to apply method chaining*/
       ModifiedBuilder startUpOperations(String startUpMessage);
       ModifiedBuilder buildBody(String bodyType);
       ModifiedBuilder insertWheels(int noOfWheels);
       ModifiedBuilder addHeadlights(int noOfHeadLights);
       ModifiedBuilder endOperations(String endOperationsMessage);
       //Combine the parts and make the final product.
       ProductClass constructCar();
       //Optional method:To get the already constructed object
       ProductClass getConstructedCar();
}
//Car class
class CarBuilder implements ModifiedBuilder
{
       private String startUpMessage="Start building the product";//Default //start-up message
       private String  bodyType="Steel";//Default body
       private int noOfWheels=4;//Default number of wheels
       private int  noOfHeadLights=2;//Default number of head lights
       //Default finish up message private String  endOperationsMessage="Product creation completed";
       ProductClass product;
       @Override
       public ModifiedBuilder startUpOperations(String startUpMessage)
       {
             this.startUpMessage=startUpMessage;
             return this;
       }
       @Override
       public ModifiedBuilder buildBody(String bodyType)
       {
             this.bodyType=bodyType;
             return this;
       }
       @Override
       public ModifiedBuilder insertWheels(int noOfWheels)
       {
             this.noOfWheels=noOfWheels;
             return this;
       }
       @Override
       public ModifiedBuilder addHeadlights(int noOfHeadLights)
       {
             this.noOfHeadLights=noOfHeadLights;
             return this;
       }
       @Override
       public ModifiedBuilder endOperations(String endOperationsMessage)
       {       this.endOperationsMessage=endOperationsMessage;
       return this;
       }
       @Override
       public ProductClass constructCar() {
             product= new ProductClass(this.startUpMessage,this.bodyType,this.noOfWheels,this.noOfHeadLights,this.endOperationsMessage);
             return product;
       }
       @Override
       public ProductClass   getConstructedCar()
 {
       return product;
 }
}
//Product class
final class ProductClass
{
       private String startUpMessage;
       private String  bodyType;
       private int noOfWheels;
       private int  noOfHeadLights;
       private String  endOperationsMessage;
       public ProductClass(final String startUpMessage, String bodyType, int noOfWheels, int noOfHeadLights,
                   String endOperationsMessage) {
             this.startUpMessage = startUpMessage;
             this.bodyType = bodyType;
             this.noOfWheels = noOfWheels;
             this.noOfHeadLights = noOfHeadLights;
             this.endOperationsMessage = endOperationsMessage;
       }
       /*There is no setter methods used here to promote immutability.
       Since the attributes are private and there is no setter methods, the keyword "final" is not needed to attach with the attributes.
        */
       @Override
       public String toString() {
             return "Product Completed as: startUpMessage=" + startUpMessage + " bodyType=" + bodyType + " noOfWheels="
                          + noOfWheels + " noOfHeadLights=" + noOfHeadLights + " endOperationsMessage=" + endOperationsMessage
                          ;
       }
}
//Director class
public class BuilderPatternModifiedExample {
       public static void main(String[] args) {
             System.out.println("***Modified Builder Pattern Demo***");
             /*Making a custom car (through builder)
               Note the steps:
               Step1:Get a builder object with required parameters
               Step2:Setter like methods are used.They will set the optional fields also.
               Step3:Invoke the constructCar() method to get the final car.
              */
             final ProductClass customCar1 = new CarBuilder().addHeadlights(5)
                          .insertWheels(5)
                          .buildBody("Plastic")
                          .constructCar();
             System.out.println(customCar1);
             System.out.println("--------------");
             /* Making another custom car (through builder) with a different
              * sequence and steps.
              */
             ModifiedBuilder carBuilder2=new CarBuilder();
             final ProductClass customCar2 = carBuilder2.insertWheels(7)
                          .addHeadlights(6)
                          .startUpOperations("I am making my own car")
                          .constructCar();
             System.out.println(customCar2);
             System.out.println("--------------");
             //Verifying the getConstructedCar() method
             final ProductClass customCar3=carBuilder2.getConstructedCar();
             System.out.println(customCar3);
       }
}

Modified Output

Here’s the modified output. (Some of the lines are bold to draw your attention to notice the differences in the output).
***Modified Builder Pattern Demo***
Product Completed as:
 startUpMessage=Start building the product
 bodyType=Plastic
 noOfWheels=5
 noOfHeadLights=5
 endOperationsMessage=Product creation completed
--------------
Product Completed as:
 startUpMessage=I am making my own car
 bodyType=Steel
 noOfWheels=7
 noOfHeadLights=6
 endOperationsMessage=Product creation completed
--------------
Product Completed as:
 startUpMessage=I am making my own car
 bodyType=Steel
 noOfWheels=7
 noOfHeadLights=6
 endOperationsMessage=Product creation completed

Analysis

Note the following lines of code (from the preceding implementation) for the custom cars creation in the client code.
System.out.println("***Modified Builder Pattern Demo***");
             /*Making a custom car (through builder)
               Note the steps:
               Step1:Get a builder object with required parameters
               Step2:Setter like methods are used.They will set the optional fields also.
               Step3:Invoke the constructCar() method to get the final car.
              */
             final ProductClass customCar1 = new CarBuilder().addHeadlights(5)
                    .insertWheels(5)
                    .buildBody("Plastic")
                    .constructCar();
             System.out.println(customCar1);
       System.out.println("--------------");
             /* Making another custom car (through builder) with a different
              * sequence and steps.
              */
             ModifiedBuilder carBuilder2=new CarBuilder();
             final ProductClass customCar2 = carBuilder2.insertWheels(7)
              .addHeadlights(6)
              .startUpOperations("I am making my own car")
              .constructCar();
       System.out.println(customCar2);
You are using a builder to create multiple objects by varying the builder attributes between calls to the “build” methods; for example, in the first case, you are invoking the addHeadLights() , insertWheels() , buildBody() methods, one by one, through a builder object, and then you are getting the final car (customCar1). But in the second case, when you create another car object (customCar2), you are invoking the methods in a different sequence. When you are not invoking any method, the default implementation is provided for you.
  1. 9.

    I am seeing the use of final keywords in client codes. But you have not used those for ProductClass attributes . What is the reason for that?

    In the client code, I used the final keywords to promote immutability. But in the ProductClass class, the attributes are already marked with private keywords and there are no setter methods, so these are already immutable.

     
  2. 10.

    What is the key benefit of immutable objects?

    Once constructed, they can be safely shared, and most importantly, they are thread safe, so you save lots in synchronization costs in a multithreaded environment.

     
  3. 11.

    When should I consider using a builder pattern?

    If you need to make a complex object that involves various steps in the construction process, and at the same time, the products need to be immutable, the builder pattern is a good choice.

     
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.184.102