Design Patterns Handbook
  • Introduction
  • Creational Patterns
    • Builder
    • Factory
    • Abstract Factory
    • Factory Method
    • Prototype
    • Singleton
    • Object Pool
    • Revealing Constructor
  • Structural Patterns
    • Adapter
    • Composite
    • Proxy
    • Flyweight
    • Facade
    • Bridge
    • Decorator
    • Private Class Data
  • Behavioral Patterns
    • Template Method
    • Mediator
    • Chain Of Responsibility
    • Observer
    • Strategy
    • Command
    • State
    • Visitor
    • Memento
    • Interpreter
    • Null Object
    • Iterator
    • Middleware
  • Clean Code Patterns
    • Extract Method
    • Clarify Responsibility
    • Remove Duplications
    • Keep Refactoring
    • Always Unit Test
    • Create Data Type
    • Comment to Better Name
    • Consistent Naming
    • If-else over ternary operator
    • Composition over Inheritance
    • Too Many Returns
    • Private to Interface
  • Anti Patterns
    • Big Ball of Mud
    • Singleton
    • Mad Scientist
    • Spaghetti Code
    • It Will Never Happen
    • Error Codes
    • Commented Code
    • Abbreviations
    • Prefixes
    • Over Patternized
    • Generic Interface over Function
Powered by GitBook
On this page
  • Example - User builder
  • Builder in JavaScript using async / await

Was this helpful?

  1. Creational Patterns

Builder

The main reason to use Builder pattern is to avoid constructors with huge amount of parameters. Some people say we should have a limit for number of parameters and it should be around 3 parameters per constructor. We should keep the number parameters around 3 because if not, it is becoming a piece of code that is difficult to read, test and manage. People, who developed feelings for a code, might say that constructors with many parameters are ugly and not friendly.

The goal when using Builder patter is to create fluent interface. Here is an example of fluent interface:

new UserBuilder().withUserName("john").withAge(21).build();

Example - User builder

Here is an example a build that constructs a user.

public class User {

    private String firstName;
    private String lastName;

    private User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public static class UserBuilder {

        private String firstName;
        private String lastName;

        public UserBuilder withFirstName(String firstName) {
            this.firstName = firstName;
            return this;
        }

        public UserBuilder withLastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public User build() {
            return new User(firstName, lastName);
        }
    }
}

Then we can use the builder's fluent interface to create a new object of User class.

User john = new User.UserBuilder()
    .withFirstName("John")
    .withLastName("Feleti")
    .build();

Builder in JavaScript using async / await

When we are writing integration tests, we might need to insert some data, that are expected to be present, into database. We can write a SQL script or put SQLs into code that would executed before each test. But then it becomes difficult to update this script. As we add more features we might need to have multiple SQL scripts to cover various scenarios. Better is to split SQL inserts into methods of builder and let the user, the one who writes the integration tests, to choose what should be inserted.

Since we want to threat the test code as production code, we should follow DRY principle. Then it becomes handy to put data creation into builders that we can reuse in test code. Here is a builder that will help us to insert data into database so we can run our integration tests.

module.exports = class {
  constructor() {
    this.promises = []
  }

  withUser() {
    this.promises.push(async () => {
      const userService = await Q.container.getAsync('userService')
      await userService.save(new User('John'))
    })
    return this
  }

  withAccount() {
    this.promises.push(async () => {
      const accountService = await Q.container.getAsync('accountService')
      await accountService.save(new Account('My Account'))
    })
    return this
  }

  async build() {
    for (let promise of this.promises) {
      await promise()
    }
  }
}

Then we can use the builder in a test as follows.

const TestDataBuilder = require('../../fixtures/test_data_builder')

describe('User Is Able to Access his Account', function() {

  beforeEach(async function() {
    await new TestDataBuilder().withUser().withAccount().build()
  })

  describe('login()', function() {
    it('fails to login because user is not linked with account', async function() {
      // write test code here
    })
  })
})
PreviousCreational PatternsNextFactory

Last updated 5 years ago

Was this helpful?