Pattern — Builder

Pattern — Builder

The pattern was originally described by Gamma et al. in his book “Design Patterns: Elements of Reusable Object-Oriented Software” (1994). In it, the authors present the?Builder?pattern as a way to separate the construction of a complex object from its representation, so that the same construction process can create different representations.

The?Builder?pattern is a build pattern that focuses on separating the construction of a complex object from its representation, so that the same build process can create different representations. It lets you build objects step by step, adding different parts or characteristics to them over time.

In general,?Builder?is used when you want to create complex objects that have many different parts or configurations and that are built through several steps.?Builder?provides an interface for specifying these steps and adding the appropriate parts or characteristics to the object.

There are two main parts involved in the?Builder?pattern, which are:

  • The Builder: which is an interface to specify the steps to create the complex object. It defines the methods for adding the different parts or characteristics of the object.
  • The Director: which is the object that controls the build process using the specified?Builder. He can create many different types of objects using the same sequence of steps, but with different parts or characteristics.

There are several advantages to using the?Builder?pattern in your code, which are:

  • Encapsulation: The?Builder?pattern allows you to hide the construction logic of a complex object inside?Builder?and the Director, instead of exposing it directly to the client. This makes the code easier to understand and maintain.
  • Flexibility: The?Builder?pattern allows you to create different representations of a complex object using the same sequence of steps. This means you can create new representations without affecting existing code.
  • Reusability: The?Builder?pattern allows you to reuse the construction logic of a complex object in different contexts. This means that you can build similar objects using the same building logic.
  • Clarity: the?Builder?pattern makes the code clearer and more organized, as it separates the construction logic from the system logic. This means that you can easily understand how an object is constructed and how it is used.
  • Simplicity: The?Builder?pattern simplifies building complex objects by providing an organized way to add different parts or characteristics to an object over time.
  • Clean code: By dividing the construction of a complex object into steps, it can make the code cleaner and easier to maintain as each step is isolated and easier to understand.

Below is a simple code example using the?Builder?pattern.

class SandwichBuilder {
    constructor() {
        this.sandwich = new Sandwich();
    }

    addBread() {
        this.sandwich.hasBread = true;
    }

    addCheese() {
        this.sandwich.hasCheese = true;
    }

    addLettuce() {
        this.sandwich.hasLettuce = true;
    }

    addMeat() {
        this.sandwich.hasMeat = true;
    }

    getSandwich() {
        return this.sandwich;
    }
}

class Sandwich {
    constructor() {
        this.hasBread = false;
        this.hasCheese = false;
        this.hasLettuce = false;
        this.hasMeat = false;
    }
}

class SandwichMaker {
    constructor() {
        this.builder = null;
    }

    setBuilder(builder) {
        this.builder = builder;
    }

    makeSandwich() {
        this.builder.addBread();
        this.builder.addCheese();
        this.builder.addLettuce();
        this.builder.addMeat();
    }
}

const sandwichBuilder = new SandwichBuilder();
const sandwichMaker = new SandwichMaker();

sandwichMaker.setBuilder(sandwichBuilder);
sandwichMaker.makeSandwich();

const mySandwich = sandwichBuilder.getSandwich();
console.log(mySandwich);        

The?SandwichBuilder?class is responsible for building an instance of the?Sandwich?class by adding different parts. It has methods for adding bread, cheese, lettuce, and meat, and a “getSandwich” method that returns the constructed sandwich object.

The?Sandwich?class is a template class that represents the sandwich object. The constructor initializes the object with all its attributes as false, and the sandwichBuilder’s addition methods will change the values.

The?SandwichMaker?class is responsible for using the?SandwichBuilder?object and making the sandwich, it is an object that controls the construction process using the specified Builder. The class has a method to set a Builder?(sandwichBuilder)?and another method to make the sandwich(makeSandwich).

Finally we start a new instance of the?SandwichBuilder?class called?sandwichBuilder. Next, we create a new instance of the?SandwichMaker?class called?sandwichMaker.

After that, we configure the?sandwichMaker?to use the?sandwichBuilder?specified with the?setBuilder(sandwichBuilder)?method. This means that the?SandwichMaker?class will use the?sandwichBuilder?to build the sandwich.

Finally, we use the?makeSandwich()?method of the?sandwichMaker?to build the sandwich and store the result in the?mySandwich?variable. Finally, we use the console.log to print the constructed sandwich to the screen. The result would be a sandwich object with all its attributes set to true.

Simple, right?

Imagine another scenario in which your team manager informed you that there is a need to consume a zip code API to automatically fill in the user’s address in the HTML form.

To solve the following problem follow the example below:

class AddressBuilder {

    constructor() {
        this.address = {};
    }

    addCEP(cep) {
        this.address.cep = cep;
    }

    addState(state) {
        this.address.state = state;
    }

    addCity(city) {
        this.address.city = city;
    }

    addStreet(street) {
        this.address.street = street;
    }

    getAddress() {
        return this.address;
    }
}

class CepFetcher {
    static url = 'https://brasilapi.com.br/api/cep/v1'
    constructor(builder) {
        this.builder = builder;
    }

    async fetchData(cep) {
        const { cep, state, city, street } = await fetch(`${url}/${this.cep}`).then(response => response.json())
        const data = { cep, state,  city, street };

        this.builder.addCEP(data.cep);
        this.builder.addState(data.state);
        this.builder.addCity(data.city);
        this.builder.addStreet(data.street);
    }
}

const builder = new AddressBuilder();
const fetcher = new CepFetcher(builder);
fetcher.fetchData('01001000');
const address = builder.getAddress();
console.log(address);        

The?AddressBuilder?class is responsible for building the address object. It has an empty constructor method that creates an empty address object. It also has methods for adding zip, state, city, and street to the data structure, and a?getAddress()?method that returns the complete address object.

The?CepFetcher?class is used to get address information from a ZIP Code API using the URL of the API. It has a builder method that takes an instance of?AddressBuilder?and a?fetchData()?method that uses the provided zip code to get the data from the API using fetch() . It then de-structures the API response to extract the zip, state, city, and street data. This data is then passed to the corresponding methods of the?AddressBuilder?class to add it to the data structure. Finally, the?fetchData?method returns the complete address object when calling the?getAddress()?method.

There are some common situations where the Builder pattern is useful, which are:

  • When you need to build objects with many optional attributes or variables, the?Builder?pattern can help you keep the code clear and simple, allowing you to build objects step by step.
  • When you need to build complex objects that require multiple steps or specific construction rules, the?Builder?pattern allows you to encapsulate this logic and easily reuse it.
  • When you need to build objects that represent hierarchical data structures, the?Builder?pattern can help you organize building objects into logical steps.
  • When you need to build objects that can be dynamically modified, the?Builder?pattern allows you to easily add or remove elements from an object.
  • When you need to create objects that consume data from an api, the pattern builder can help organize and keep the object construction logic consistent with the data returned by the api.

Conclusion

The?Builder?pattern allows you to keep your code clean and simple, especially when working with complex objects with many optional attributes or variables.

Builder?can be useful for many situations, some of them include:

  • When you need to build complex objects that require multiple steps or specific construction rules, the Builder pattern lets you encapsulate that logic and easily reuse it.
  • When you need to build objects that can be dynamically modified, the Builder pattern allows you to easily add or remove elements from an object.
  • When you need to create objects that consume data from an API, the Builder pattern can help you organize and keep object construction logic consistent with the data returned by the API.
  • When you need to build objects from different types of data sources (files, api, database) and need to keep the construction logic consistent, pattern builder can be a great choice.

Hope this helps, until next time.

要查看或添加评论,请登录

Higor Diego的更多文章

社区洞察

其他会员也浏览了