Skip to content

Unleashing Flexibility with the Builder Design Pattern in Functional Programming

Posted on:August 26, 2023 at 01:13 PM

Welcome back! In this lecture, we’re diving into the realm of the Builder design pattern in the context of functional programming. The Builder pattern is a powerful tool that provides flexibility in the creation of complex objects. In situations where objects require numerous arguments for construction or a plethora of setters, the Builder pattern emerges as a flexible solution. Furthermore, it ensures the immutability of objects after their creation, enhancing code robustness.

Table of contents

Open Table of contents

Sections

The Essence of the Builder Pattern

The Builder pattern brings simplicity and control to the process of object creation. It’s particularly useful when dealing with complex object structures and a multitude of construction parameters. This pattern tackles the challenges of managing constructors overloaded with different parameter combinations or managing excessive setter methods, while also enforcing immutability once the object is constructed.

In the world of functional programming, we harness the power of higher-order functions and lambdas to elegantly implement the Builder pattern. By doing so, we adhere to the principles of functional programming, such as immutability and declarative programming.

Deciphering the UML Diagram

Before we delve into the functional implementation of the Builder pattern, let’s take a moment to revisit the UML diagram that represents the pattern:

builderUML

In this diagram, the “Product” signifies the complex object that will be created. The “Builder” defines the steps required to correctly construct the product. There can be multiple concrete builders, each designed to create a particular variant of the complex product. The “Director” class orchestrates the creation algorithm, specifying the concrete builder to be used and invoking the necessary methods in the correct sequence. The final product is then obtained by invoking the “getCompleteObject” (or “getCompleteProduct”) method.

Functional Implementation of the Builder Pattern

Now, let’s put theory into practice and explore a functional programming example of the Builder pattern. Imagine we’re constructing mobile devices, each with various attributes such as RAM, storage, battery, camera, processor, and screen size. To maintain immutability and streamline the object creation process, we’ll employ the Builder pattern.

Here’s the code example:

class Mobile {
    private final String RAM;
    private final String storage;
    private final String battery;
    private final String camera;
    private final String processor;
    private final String screenSize;

    public Mobile(MobileBuilder builder) {
        this.RAM = builder.RAM;
        this.storage = builder.storage;
        this.battery = builder.battery;
        this.camera = builder.camera;
        this.processor = builder.processor;
        this.screenSize = builder.screenSize;
    }

    // Getters and toString method
}

class MobileBuilder {
    String RAM = "2GB"; // Default value
    String storage = "16GB"; // Default value
    String battery = "3000mAh"; // Default value
    String camera = "8MP"; // Default value
    String processor = "Snapdragon"; // Default value
    String screenSize = "5 inches"; // Default value

    public MobileBuilder with(Consumer<MobileBuilder> buildFields) {
        buildFields.accept(this);
        return this;
    }

    public Mobile createMobile() {
        return new Mobile(this);
    }
}

public class BuilderPatternDemo {
    public static void main(String[] args) {
        MobileBuilder builder = new MobileBuilder();
        Mobile myMobile = builder
            .with(mb -> {
                mb.RAM = "4GB";
                mb.battery = "4000mAh";
                mb.processor = "A12 Bionic";
            })
            .createMobile();

        System.out.println(myMobile);
    }
}

In this example, the Mobile class represents our product, with its attributes defined in a constructor. We’ve made the attributes final to ensure immutability. The MobileBuilder class encapsulates the creation process. Using a higher-order function called with, we set the values of the attributes provided by the client. The createMobile method constructs the final mobile object.

Wrapping Up

The Builder pattern offers a graceful solution to the challenges of object creation, especially in the presence of numerous attributes or constructor variations. In the functional programming paradigm, we’ve harnessed the power of higher-order functions to implement the Builder pattern elegantly and immutably. This pattern not only ensures code readability but also enhances the maintainability and scalability of applications.

In our next session, we’ll explore another intriguing pattern in functional programming. Until then, keep coding and experimenting!

You can find the repo for this section of the course Here