Design Pattern: Builder Pattern in TypeScript

Builder pattern simplifies the object-building process by keeping the object creation and usage process separate. Also helpful in the case when the item class has a lot of properties, by setting the properties step by step.

This article demonstrates Builder pattern implementations in TypeScript. Check the following examples.

Implementation

These are the common key points for implementing Builder pattern in TypeScript.

  • Create classes for the items. 
  • Define only getter methods for the properties, in the item classes.
  • Define a builder class for each item class.
  • Declare the same properties in the builder, as the item class.
  • Define the setter method for the properties, in the builder class.
  • Define a builder method in the builder class, usually named “build”. In the “build” method- construct an object of the item class (pass the property values are needed).
  • For usage – create a builder object, then set properties and set the builder method. That will return an item object. We can then use the item object.

Here is a simple example of the builder pattern. Take a look at the code.

// Item class
class Item {
    private property1: number;
    private property2: number;

    constructor(property1: number, property2: number) {
        this.property1 = property1;
        this.property2 = property2;
    }

    getProperty1(): number {
        return this.property1;
    }

    getProperty2(): number {
        return this.property2;
    }
}

// Builder class
class Builder {
    private property1!: number;
    private property2!: number;

    setProperty1(property1: number): Builder {
        this.property1 = property1;
        return this;
    }

    setProperty2(property2: number): Builder {
        this.property2 = property2;
        return this;
    }

    build(): Item {
        return new Item(this.property1, this.property2);
    }
}

// Demo
const builder = new Builder();
const item = builder.setProperty1(200)
    .setProperty2(9999)
    .build();

console.log(item.getProperty1(), item.getProperty2())

We will get the following output when above code is executed:

200 9999

Examples

Here are a few examples of builder pattern implementation in TypeScript.

Example #1: Vehicle Builder

In this example we have few item class – Car, Plane. We want to create item objects using the builder pattern.

Car Item Class

  • Create a file named “car.ts”.
  • Create class “Car”.
  • Define properties and define only the getter methods for the properties.
  • In constructor accept values of properties and set the values to properties.
// car.ts

class Car {
    private wheel: number;
    private engine: number;
    private seat: number;
    private door: number;
    private interior: boolean;

    constructor(noOfWheel: number, noOfEngine: number, noOfSeat: number, noOfDoor: number, interior: boolean) {
        this.wheel = noOfWheel;
        this.engine = noOfEngine;
        this.seat = noOfSeat;
        this.door = noOfDoor;
        this.interior = interior;
    }

    getWheel(): number {
        return this.wheel;
    }

    getEngine(): number {
        return this.engine;
    }

    getSeat(): number {
        return this.seat;
    }

    getDoor(): number {
        return this.door;
    }

    isInterior(): boolean {
        return this.interior;
    }

    toString(): string {
        return `Car:
        Wheel -> ${this.wheel}
        Engine -> ${this.engine}
        Seat -> ${this.seat}
        Door -> ${this.door}
        Interior -> ${this.interior}`;
    }
}

export default Car;

Plane Item Class

  • Create a file named “plane.ts”.
  • Create item class “Plane”.
  • Define properties and getter methods.
  • In the constructor accept param and set property values.
// plane.ts

class Plane {
    private wheel: number;
    private engine: number;
    private seat: number;
    private door: number;
    private wing: number;
    private interior: boolean;

    constructor(
        noOfWheel: number,
        noOfEngine: number,
        noOfSeat: number,
        noOfDoor: number,
        wing: number,
        interior: boolean) {
        this.wheel = noOfWheel;
        this.engine = noOfEngine;
        this.seat = noOfSeat;
        this.door = noOfDoor;
        this.wing = wing;
        this.interior = interior;
    }

    getWheel(): number {
        return this.wheel;
    }

    getEngine(): number {
        return this.engine;
    }

    getSeat(): number {
        return this.seat;
    }

    getDoor(): number {
        return this.door;
    }

    getWing(): number {
        return this.wing;
    }

    isInterior(): boolean {
        return this.interior;
    }

    toString(): string {
        return `Plane:
        Wheel -> ${this.wheel}
        Engine -> ${this.engine}
        Seat -> ${this.seat}
        Door -> ${this.door}
        Wing -> ${this.wing}
        Interior -> ${this.interior}`;
    }
}

export default Plane;

Vehicle Builder Interface

  • Create file “vehicle-builder.ts”.
  • Define interface “VehicleBuilder”.
  • Declare setter methods for the properties.
// vehicle-builder.ts

interface VehicleBuilder {
    addWheel(noOfWheel:number): void;
    addEngine(noOfEngine:number): void;
    addSeat(noOfSeat:number): void;
    addInterior(): void;
    addDoor(noOfDoor:number): void;
    addWing(noOfWing:number): void;
}

export default VehicleBuilder;

Car Builder Class

  • Create file named “car-builder.ts”.
  • Define class “CarBuilder”.
  • Implement interface “VehicleBuilder” for the “CarBuilder” class.
  • Define a method named “build”, and in the method create a new Car object and return that.
// car-builder.ts

import Car from "./car";
import VehicleBuilder from "./vehicle-builder";


class CarBuilder implements VehicleBuilder {
    private wheel: number = 0;
    private engine: number = 0;
    private seat!: number;
    private interior!: boolean;
    private door: number = 0;

    addWheel(noOfWheel: number) {
        console.log("Add " + noOfWheel + " wheels");
        this.wheel += noOfWheel;
    }

    addEngine(noOfEngine: number) {
        console.log("Add " + noOfEngine + " engine");
        this.engine += noOfEngine;
    }

    addSeat(noOfSeat: number) {
        console.log("Add " + noOfSeat + " Seat");
        this.seat = noOfSeat;
    }

    addInterior() {
        console.log("Add interior");
        this.interior = true;
    }

    addDoor(noOfDoor: number) {
        console.log("Add " + noOfDoor + " door");
        this.door += noOfDoor;
    }

    addWing(noOfWings: number) {
        throw new Error("Can not add wings");
    }

    build(): Car {
        const car: Car = new Car(this.wheel, this.engine, this.seat, this.door, this.interior);

        return car;
    }
}

export default CarBuilder;

Plane Builder Class

Create a builder class for Plane. Follow the same/similar steps mentioned for the “CarBuilder”.

  • Create file named “plane-builder.ts”.
  • Define class “PlaneBuilder”.
  • Implement interface “VehicleBuilder”.
  • Define the build method which creates a new “Plane” object and returns that.
// plane-builder.ts

import Plane from "./plane";
import VehicleBuilder from "./vehicle-builder";

class PlaneBuilder implements VehicleBuilder{
    private wheel: number = 0;
    private engine: number = 0;
    private seat!: number;
    private interior!: boolean;
    private door: number = 0;
    private wing: number = 0;

    addWheel(noOfWheel: number) {
        console.log("Add " + noOfWheel + " wheels");
        this.wheel += noOfWheel;
    }

    addEngine(noOfEngine: number) {
        console.log("Add " + noOfEngine + " engine");
        this.engine += noOfEngine;
    }

    addSeat(noOfSeat: number) {
        console.log("Add " + noOfSeat + " Seat");
        this.seat = noOfSeat;
    }

    addInterior() {
        console.log("Add interior");
        this.interior = true;
    }

    addDoor(noOfDoor: number) {
        console.log("Add " + noOfDoor + " door");
        this.door += noOfDoor;
    }

    addWing(noOfWings: number) {
        console.log("Add " + noOfWings + " wing");
        this.wing += noOfWings;
    }

    build(): Plane {
        const plane: Plane = new Plane(this.wheel, this.engine, this.seat, this.door, this.wing, this.interior);

        return plane;
    }
}

export default PlaneBuilder;

Vehicle Producer Class

If you want, then ignore the Producer and write all these codes in your usage part.

Create a producer for the vehicles.

  • Create a class named “vehicle-producer.ts”.
  • Define class “VehicleProducer”.
  • Define method for building car and plane.
  • In the builder methods set values for properties and return the builder at the end.
// vehicle-producer.ts

import CarBuilder from "./car-builder";
import PlaneBuilder from "./plane-builder";

class VehicleProducer {
    buildCar(carBuilder: CarBuilder): CarBuilder {
        carBuilder.addWheel(4);
        carBuilder.addEngine(1);
        carBuilder.addDoor(4);
        carBuilder.addSeat(4);
        carBuilder.addInterior();

        return carBuilder;
    }

    buildPlane(planeBuilder: PlaneBuilder): PlaneBuilder {
        planeBuilder.addWheel(3);
        planeBuilder.addEngine(2);
        planeBuilder.addDoor(4);
        planeBuilder.addSeat(120);
        planeBuilder.addInterior();
        planeBuilder.addWing(2);
        
        return planeBuilder;
    }
}

export default VehicleProducer;

Demo

  • Create a file “demo.ts”.
  • Create an object of “VehicleProducer”.
  • Use the build method of the producer to set the values.
  • Call the build method of the builder to create an item object.
// demo.ts

import Car from "./car";
import CarBuilder from "./car-builder";
import Plane from "./plane";
import PlaneBuilder from "./plane-builder";
import VehicleProducer from "./vehicle-producer";

const vehicleProducer = new VehicleProducer();

console.log("Building Car:\n");

const carBuilder = new CarBuilder();
vehicleProducer.buildCar(carBuilder);

const car = carBuilder.build();
console.log("Final result:\n" + car);


console.log("Building Car:\n");

const planeBuilder = new PlaneBuilder();
vehicleProducer.buildPlane(planeBuilder);

const plane = planeBuilder.build();
console.log("Final result:\n" + plane);

Output

Output of the above demo code will be as below.

Building Car:

Add 4 wheels
Add 1 engine
Add 4 door
Add 4 Seat
Add interior

Final result:
Car:
        Wheel -> 4
        Engine -> 1
        Seat -> 4
        Door -> 4
        Interior -> true


Building Car:

Add 3 wheels
Add 2 engine
Add 4 door
Add 120 Seat
Add interior
Add 2 wing

Final result:
Plane:
        Wheel -> 3
        Engine -> 2
        Seat -> 120
        Door -> 4
        Wing -> 2
        Interior -> true

Example #2: Request Builder (Using inner Builder)

In the next example, we will implement the Builder pattern for making HTTP requests (dummy). This will enable us to build the request step-by-step, and finally build the request object.

Request Class with Builder

  • Create file named “http-request.ts”.
  • Create a class named “HttpRequest”.
  • Create a builder class inside the “HttpRequest”, name that class “Builder”.
  • Make the builder class static.
// http-request.ts

export enum RequestType {
    GET,
    POST,
    PUT,
    PATCH,
    DELETE
}

class HttpRequest {
    private url: string;
    private type: RequestType;
    private header = new Map<string, string>();
    private body = new Map<string, string>();

    constructor(
        url: string,
        type: RequestType,
        header: Map<string, string>,
        body: Map<string, string>
        ) {
        this.url = url;
        this.type = type;
        this.header = header;
        this.body = body;
    }

    public send() {
        console.log("Sending Request...");
        console.log("URL: ", this.url);
        console.log("Type: ", this.type);
        console.log("Header: ", this.header);
        console.log("Body: ", this.body);
        
        // Write code to send the request here
    }

    static Builder = class {
        private url: string = "";
        private type: RequestType = RequestType.GET;
        private header = new Map<string, string>();
        private body = new Map<string, string>();

        public setUrl(url: string) {
            this.url = url;
            return this;
        }

        public setType(type: RequestType) {
            this.type = type;
            return this;
        }

        public setHeader(key: string, value: string) {
            this.header.set(key, value);
            return this;
        }

        public setBody(key: string, value: string) {
            this.body.set(key, value);
            return this;
        }

        public build() {
            return new HttpRequest(this.url, this.type, this.header, this.body);
        }
    }
}

export default HttpRequest;

Demo

Here is the process of how to use the builder pattern implementation –

  • Create a file named “demo.ts”.
  • Use “new HttpRequest.Builder()” for initiating the builder.
  • Set values for the builder.
  • Finally, call the “build()” method to build the request object. Save the request object in “request” constant.
  • We can then use the “request.send()” to call the send method of “HttpRequest”.
// demo.ts

import HttpRequest, { RequestType } from "./http-request";

const request = new HttpRequest.Builder()
    .setUrl("https://bigboxcode.com/request-test")
    .setType(RequestType.POST)
    .setHeader("X-AUTH-TOKEN", "someTokeHere")
    .setHeader("X-SOME-HEADER", "someRandomHeaderValueHere")
    .setBody("unit_id", "99")
    .setBody("code", "88C3ABK")
    .build();

// Send request
request.send();

Output

Output of the above code will be as below, as we are printing the values of the object.

Sending Request...

URL:  https://bigboxcode.com/request-test
Type:  1

Header:  Map(2) {
  'X-AUTH-TOKEN' => 'someTokeHere',
  'X-SOME-HEADER' => 'someRandomHeaderValueHere'
}

Body:  Map(2) {
  'unit_id' => '99',
  'code' => '88C3ABK'
}

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check Builder Pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.