Implementing the builder pattern

The Builder pattern is a creational pattern whose goal is to simplify object construction. Instead of having complex constructors that take a large number of parameters, this pattern provides ways to create objects with different states of representation. It gives flexibility while instantiating a class. It provides builder functions to construct the object step by step, and the object will be returned in the final step.

Consider a class that represents a person. We will have to create person objects with different combinations of characteristics. Say, for example, person with firstName, lastName, age, contactNumber, loginId, and address. The class looks as follows:

class Person {
var firstName: String? = null
var lastName: String? = null
var middleName: String? = null
var loginId: String? = null
var age: Int? = null
var contactNumber: String? = null
var address: String? = null

constructor(firstName: String?, lastName: String?, middleName: String?, loginId: String?, age: Int?,
contactNumber: String?, address: String?) {
this.firstName = firstName
this.lastName = lastName
this.middleName = middleName
this.loginId = loginId
this.age = age
this.contactNumber = contactNumber
this.address = address
}
}

To create an instance of the Person type, let's say, we provide a complex constructor that takes all of these parameters. If we want to create a person object, we have to invoke the constructor by passing all the fields, as shown here:

object Test {
@JvmStatic
fun main(args: Array<String>) {
val person = Person("Jane", "", "", "[email protected]", 21, "4561298421", "10, Charles ", "Street,NY")
}
}

The problem with this approach is that the list of parameters supplied to create an object is long, and chances are we may pass the parameters in the wrong order if the parameters are of same type. This is slightly confusing. Also, sometimes all of the information may not be required or not available to create a Person object.

One way to solve this problem is to have multiple constructors, such as one that takes firstName, lastName, and then if needed another one that takes firstName, lastName, loginId. There could be yet another one that takes the first three arguments and age, and finally one constructor that takes all of the parameters. The problem with this approach is that the class ends up in having multiple constructors. As the parameter list grows, for every possible combination the number of constructors will also increase:

constructor(firstName: String?) {
this.firstName = firstName
}

constructor(firstName: String?, lastName: String?) {
this.firstName = firstName
this.lastName = lastName
}

constructor(firstName: String?, lastName: String?, middleName: String?) {
this.firstName = firstName
this.lastName = lastName
this.middleName = middleName
}

constructor(firstName: String?, lastName: String?, loginId: String?, contactNumber: String?) {
this.firstName = firstName
this.lastName = lastName
this.loginId = loginId
this.contactNumber = contactNumber
}
constructor(firstName: String?, lastName: String?, middleName: String?, loginId: String?, age: Int?,
contactNumber: String?, address: String?) {
this.firstName = firstName
this.lastName = lastName
this.middleName = middleName
this.loginId = loginId
this.age = age
this.contactNumber = contactNumber
this.address = address
}

The Builder pattern is a better alternative to solve this problem. The Builder pattern moves the construction of complex objects out of the constructor and provides flexibility in creating objects. Classes that use the builder pattern scale better, and it improves readability.

The Builder pattern consists of three components: Product, Director, and a Builder, as shown in the following diagram:

Product is a complex structure that needs to be constructed. In our case, the Person class is the product. A Director is a class that creates objects using the builder class. The Test class is the director in our case. Now we need to create a builder class that is used to build the components of the complex Person object.

The builder class looks as follows:

class PersonBuilder:Builder {
var firstName: String? = null
var lastName: String? = null
var middleName: String? = null
var loginId: String? = null
var age: Int? = null
var contactNumber: String? = null
var address: String? = null

fun withFirstName(firstName: String): PersonBuilder {
this.firstName = firstName
return this
}

fun withLastName(lastName: String): PersonBuilder {
this.lastName = lastName
return this
}

fun withMiddleName(middleName: String): PersonBuilder {
this.middleName = middleName
return this
}

fun withLoginId(loginId: String): PersonBuilder {
this.loginId = loginId
return this
}

fun withAge(age: Int): PersonBuilder {
this.age = age
return this
}

fun withContactNumber(contactNumber: String): PersonBuilder {
this.contactNumber = contactNumber
return this
}

fun withAddress(address: String): PersonBuilder {
this.address = address
return this
}

fun build(): Person {
return Person(firstName, lastName, middleName, loginId, age, contactNumber, address)
}
}

In the Test class that creates the Person object, we now have the flexibility to use these builder functions than the constructor that take all the parameters.

Let's rewrite the code in the Test class:

var person = PersonBuilder()
.withFirstName("Jane")
.withLoginId("[email protected]")
.withContactNumber("4561298421")
.build();

We implemented the builder pattern for our Person class, which gives flexibility to the classes creating the Person object to create it with the different argument list. Thus, we simplified the creation of a complex object with the Builder pattern.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset