Today we will discussing a very important pattern in design pattern called as builder pattern, there are class of problem which can be solved using this pattern effectively and efficiently without cluttering the client code which uses those classes.
First of all let’s understand why we need builder pattern and what pattern they solve.
Typical way to instantiate a class is to use its constructor. A constructor is set of instructions which sets up the object for you with a specific state to start with. As we all know, a constructor can take parameters to it. If no constructor is defined for class, instantiate using default constructor which does not take any input parameter. Constructors with parameter set values for the member variables of class. However, many a times only a few member variables of class are mandatory at the time of object creation. Rest all are optional. So, how do we create an object of such class? We cannot have constructor with only parameters corresponding to mandatory member variables, as in some cases optional variables are also required to create object correctly.
Example of such a class which has some mandatory and rest optional member variables is FoodItem. It can have some mandatory members like MRP, Manufacturing date, Batch number, and some optional members like nutrients like total fat, saturated fat, trans fat, cholesterol, sodium, MSG, proteins and so on. Not every FoodItem object should have values for total fat, saturated fat, trans fat, cholesterol, sodium or MSG or proteins.
There are two ways to solve this problem part from builder pattern. Let’s look at them before we move to builder patterns.
First one is to create telescoping constructors. In this we create a default constructor, a constructor with all mandatory member variables and then create a constructor adding each optional variable to it. First constructor with one optional parameter, second with two, third with three and so on. So there are four mandatory and 20 optional, we would end up creating 1 + 20 constructor for the class. While creating object, one need to call constructor with minimum parameters matching the requirement. Many times the constructor with more number of parameters needs to be used because you want to set optional parameter which is after non required parameter in constructor parameter list. In that case, all non required optional parameters are set to zero. This makes code very unmanageable on the client side who needs to create objects of you class.
Telescopic constructor pattern solves problem where one need to send parameters which are not required for object creation. However, it comes with a caveat, if two parameters next to each other are same type, then putting values for one parameter in other is a common mistake and will not result in compile time warning, but may not create desired object. This method is not scalable.
Second method is to have a constructor with mandatory members and the a setter function for each parameter. At the time of object creation, only mandatory parameters are passed and setter methods are used to set values for required optional parameters.
There are some problems with this approach. One, use of setter, makes your class mutable. So beware of using this kind of objects in multi-threaded applications. Second, during creation of object, there are intermediate states of object before the final object is created. However, the object can be used by other threads before setters of optional values are executed and a incomplete object is seen by the new thread.
There is third way which comes with best of above two and deals with disadvantages of them too. This pattern is called as build pattern. In this pattern, we create a static member class of our class called as builder. First we create a builder class with all the mandatory parameters and the using that object set values of optional fields and then finally call build function which creates the object of our class. Let’s take the same example as above and use builder pattern to create an object.
Client Code :There are two advantages of this approach : First we are not putting setter function on member variables of class but on builder class which makes our class as immutable. Second advantage is our final object is created in on instruction, and not in series of instructions like in second method, so there is no risk of inconsistent copies object in different threads.
This is not the only use of builder pattern. Many a times we need to create an object but the contents of those object can be different. For example, one wants to create a Menu for a fast food restaurant which offer meals. Now,meals can be veg and no-veg. Based on request either meal object contains veg dishes or non-veg dishes. Here we have to create an same object but with slight difference. This cannot be achieved by using constructor as in constructor we need to pass all the member variable and in this case one of the variable will be not use either veg or non-veg item.
This is where builder comes into play. We create a builder which returns us a meal based on inputs we have given. It combines items into meal and return that back. Let’s take an example and see.
We have veg meal which contain a veg burger and a cold drink. Non veg meal has chicken burger and ice tea.
First of all, we will create an interface which will be implemented by the solid food and drink as well.
Create a class burger which implements above interface and extend it to create veg burger and chicken burger. Cool that is first part! Similarly we can create a class drink and based on type of meal we can create object with appropriate values.
Veg burger class
Chicken Burger class
Now, create a cold drink class which implements FoodItem interface and extend it to create classes Pepsi and Ice Tea.
Ice Tea class
Now we create a builder which creates meals for us using these objects.
To use this build, we write a client code which creates veg meal and non-veg meal based on requirement.
There are some disadvantages with builder pattern, first being lot of extraneous code, it may be possible that developer skips adding any of new field into builder and problems occur. However, the best advantage that it keeps class as immutable given multi-threaded world we live in, overcomes the extra lines of code which is being written.