Design Patterns – Creational – Builder Pattern – Remove Boiler Plate Code with Lombok

Builder Pattern is the most widely used creational pattern. It’s used to provide a flexible solution to various object creation problems in object-oriented programming. The intent of the Builder design pattern is to separate the construction of a complex object from its representation.
Separating the object construction would need a new Builder class to be written for every complex Type in the application. Lot of boilerplate code is needed for its implementation. Everytime the complex Type is updated with new members, the builder would also need to be updated to handle these new members.

Below is the implementation of the Builder Pattern for a complex Type PhoneBlok.

package com.codeskittles.learning.designpatterns.creational.builder.lombok;

// Complex type
public class _01_PhoneBlokWithoutLombok {

    private final String camera;
    private final String projector;
    private final String processor;
    private final String internalMemory;
    private final String battery;
    private final String displayScreen;

    // The Boiler Plate Code section below has to be written for every complex type in the application.
    // Everytime _01_PhoneBlokWithoutLombok undergoes a change where new class members are added, the 
    // Builder also needs to undergo a change. This reduces maintainability.
    
    // Boiler Plate Code - Start
    private _01_PhoneBlokWithoutLombok(final Builder builder) {
        this.processor = builder.getProcessor();
        this.internalMemory = builder.getInternalMemory();
        this.battery = builder.getBattery();
        this.displayScreen = builder.getDisplayScreen();
        this.camera = builder.getCamera();
        this.projector = builder.getProjector();
    }

    private static class Builder {

        private String processor;
        private String internalMemory;
        private String battery;
        private String displayScreen;
        private String camera;
        private String projector;

        public Builder processor(final String processor) {
            this.processor = processor;
            return this;
        }

        public Builder internalMemory(final String internalMemory) {
            this.internalMemory = internalMemory;
            return this;
        }

        public Builder battery(final String battery) {
            this.battery = battery;
            return this;
        }

        public Builder displayScreen(final String displayScreen) {
            this.displayScreen = displayScreen;
            return this;
        }

        public Builder camera(final String camera) {
            this.camera = camera;
            return this;
        }

        public Builder projector(final String projector) {
            this.projector = projector;
            return this;
        }

        public _01_PhoneBlokWithoutLombok build() {
            return new _01_PhoneBlokWithoutLombok(this);
        }

        private String getProcessor() {
            return processor;
        }

        private String getInternalMemory() {
            return internalMemory;
        }

        private String getBattery() {
            return battery;
        }

        private String getDisplayScreen() {
            return displayScreen;
        }

        private String getCamera() {
            return camera;
        }

        private String getProjector() {
            return projector;
        }
    }

    public static Builder getBuilder() {
        return new Builder();
    }

    // Boiler Plate Code - End

    public static void main(String[] args) {

        // Clients of PhoneBlokWithoutLombok

        final _01_PhoneBlokWithoutLombok bareBonesPhone =
                _01_PhoneBlokWithoutLombok.getBuilder()
                        .processor("Intel i9 10th gen")
                        .internalMemory("16gb Samsung RAM")
                        .battery("4000mAh Battery")
                        .displayScreen("UHD 4K Screen")
                        .build();

        final _01_PhoneBlokWithoutLombok withProjectorAndNoCamera =
                _01_PhoneBlokWithoutLombok.getBuilder()
                        .processor("Intel i9 10th gen")
                        .internalMemory("16gb Samsung RAM")
                        .battery("4000mAh Battery")
                        .displayScreen("UHD 4K Screen")
                        .projector("Elite Projector 4K")
                        .build();
    }
}

There are several open-source libraries (Lombok, FreeBuilder etc) that help developers avoid implementing this boiler plate. These libraries uses Java’s annotation processing to generate a concrete implementation of the Builder Pattern.

Builder using Lombok

The Lombok library has an annotation @Builder which can placed on the Type (can also be placed on a method or a constructor) which would instruct the Java Compiler to generate a Builder for the Type.

To use Lombok, we would need to add the Maven/Gradle Dependency into the project.

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>  <!-- Latest version at the time of writing this. -->
            <scope>provided</scope>
        </dependency>

If you are using an IDE like Eclipse or Intellij you would need to add Lombok as a plugin and enable Annotation Processing for it so that your IDE does not complain.

Intellij
File -> Settings -> Plugins -> Search for Lombok -> Install -> Restart

package com.codeskittles.learning.designpatterns.creational.builder.lombok;

import lombok.Builder;

// Lombok's Builder Annotation
@Builder
public class _02_PhoneBlokWithLombok {

    private final String camera;
    private final String projector;
    private final String processor;
    private final String internalMemory;
    private final String battery;
    private final String displayScreen;

    // No need to write the Builder Implementation. Lombok does it for you,
    // just annotate your type you will have a builder
    public static void main(String[] args) {
        // Client of _02_PhoneBlokWithLombok

        _02_PhoneBlokWithLombok phoneBlok =
                _02_PhoneBlokWithLombok
                        // The methods below were auto generated by Lombok
                        .builder()
                        .processor("Intel i9 10th gen")
                        .internalMemory("16gb Samsung RAM")
                        .battery("4000mAh Battery")
                        .displayScreen("UHD 4K Screen")
                        .projector("Elite Projector 4K")
                        .build();
    }
}

Compiling the code above would generate 2 class files: one for the type and one for its builder.

If you see the code above, you would notice that you did not have to write all that Boiler plate code, Lombok generates everything for you.
Lombok has several other useful annotations, would highly recommend using them if they suit your use-case.

All the code is available on Github.
Repo Co-ordinates : https://github.com/roshanvinil89/learning.git