Today I will be discussing about the Builder Design Pattern. This is continuation of the Design Pattern Series. Builder is a creational design pattern which is used to abstract the steps of construction of complex object. The different implementations of these steps can be used to construct different representations of object. We will be using an example of processing a bill.
Problem Statement
Assume we are building a software for a service provider which provides multiple services including mobile phone service, landline phone service, satellite TV cable service and broadband services. Customers can opt for one or more services. Depending on the chosen services the monthly bill is generated for each customer. Lets assume that a customer is a very loyal customer of this service provider and has chosen to avail all the services. We need to build the software which generates the bill based on certain business rules.
The final bill contains the summary information displaying the customer details, address of the customer, duration for which the bill is calculated, last date for bill payment. Apart from this the bill also contains the detailed summary of each of the services and applicable charges along with services taxes applicable on them. And finally the loyalty points are displayed towards the end of the bill.
I have based this example of the Starhub bill model which is a service provider in Singapore. It is similar to any other service provider in other countries. You can relate it to others like Airtel or BSNL in India. And I am sure it will be the same in any other country for the providers who offer multiple services to customers.
Use Builder Pattern to construct object
In this case if we consider Bill as a top level domain object, then there are various other objects which form part of the bill. We can break it down further into smaller manageable objects like Customer, previous months bill, current months bill for phone, landline, cable TV and broadband. And there is also they loyalty points. We can build these objects step by step using a builder which is dedicated to defining the steps in which each of the constituents are to be constructed.
public class Bill
{
public Customer Customer { get; set; }
public BillSummary PreviousBillSummary { get; set; }
public BillSummary CurrentBillSummary { get; set; }
public PhoneBill CellPhoneBill { get; set; }
public PhoneBill LandlinePhoneBill { get; set; }
public CabelTVBill CabelTVBill { get; set; }
public BroadbandBill BroadbandBill { get; set; }
}
As we can see from the above code, the Bill class acts as an container for the other objects like Customer, PreviousBillSummary, CurrentBillSummary etc. In actual projects these objects would be populated either from database or from different services. For simplicity I am going to hard code these values.
We can define the process of populating these objects as a series of steps. These steps are encapsulated into an interface. The interface also provides a way of accessing the final composite object. In the below code snippet, this is achieved using the Bill property.
public interface IBillBuilder
{
Bill Bill { get; set; }
void BuildPreviousBill();
void BuildCurrentBillSummary();
void BuildCustomerDetails();
void BuildCellPhoneBill();
void BuildLandlinePhoneBill();
void BuildCableTVBill();
void BuildBroadbandBill();
void BuildLoyaltyPointsDetails();
}
Here is an implementation of the IBillBuilder interface.
public class BillBuilder : IBillBuilder
{
public BillBuilder()
{
Bill = new Bill();
}
public Bill Bill { get; set; }
public void BuildPreviousBill()
{
Bill.PreviousBillSummary = new BillSummary
{
Amount = 100,
PaidOn = DateTime.Now.Add(TimeSpan.FromDays(-15))
};
}
public void BuildCurrentBillSummary()
{
Bill.CurrentBillSummary = new BillSummary
{
Amount = 125,
DueDate = DateTime.Now.AddDays(TimeSpan.FromDays(21).Days)
};
}
public void BuildCustomerDetails()
{
Bill.Customer = new Customer { Name = "James Bond", Address = "Jupitor", AccountNumber = 10012345 };
}
public void BuildCellPhoneBill()
{
Bill.CellPhoneBill = new PhoneBill { BillType = "Cell Phone", Amount = 40 };
}
public void BuildLandlinePhoneBill()
{
throw new NotImplementedException();
}
public void BuildCableTVBill()
{
throw new NotImplementedException();
}
public void BuildBroadbandBill()
{
throw new NotImplementedException();
}
public void BuildLoyaltyPointsDetails()
{
throw new NotImplementedException();
}
}
I have implemented only few methods like BuildCustomerDetails, BuildPreviousBill, BuildCurrentBillSummary, BuildCellPhineBill. These methods are responsible for populating the composite objects. Once we have these methods ready its a matter of calling them in the correct sequence. This is where an external class commonly referred to as Director comes into the picture. The Gang of Four book describes this class as director because it is in charge of defining the sequence in which the Bill is to be constructed.
public class BillGenerator
{
private readonly IBillBuilder _billBuilder;
public BillGenerator(IBillBuilder billBuilder)
{
_billBuilder = billBuilder;
}
public Bill Bill
{
get
{
return _billBuilder.Bill;
}
}
public void GenerateBill()
{
_billBuilder.BuildCustomerDetails();
_billBuilder.BuildPreviousBill();
_billBuilder.BuildCurrentBillSummary();
_billBuilder.BuildCellPhoneBill();
_billBuilder.BuildLandlinePhoneBill();
_billBuilder.BuildCableTVBill();
_billBuilder.BuildBroadbandBill();
_billBuilder.BuildLoyaltyPointsDetails();
}
}
The GenerateBill method is used by BillGenerator to define the sequence of constructing the Bill using an instance of class which implements IBillBuilder interface. Using this approach we have constructed a composite instance of Bill class but the steps of creating this instance is defined by the BillGenerator class which acts as the Director.
Conclusion
We saw how to use Builder pattern to separate the construction of a complex object by defining the steps using a Director. The advantage of using this approach is we can follow the same steps or the sequence to build a different representation of the Bill. Assume we have a corporate customer and a non-corporate customer. We can use the same sequence of steps to build bills for both corporate and normal customers.
Many a times we get confused between the Abstract Factory and the Builder design pattern as both are creational patterns. Once again the intent of the pattern is helpful in deciding which pattern to choose. Abstract Factory return a family of related objects like SqlConnection, SqlCommand or OracleConnection, OracleCommand etc. Builder is used to build a complex or a composite object using a step by step process.
As always the complete source code is available for download BuilderDesignPatternDemo.zip
Until next time Happy Programming.
Further Reading
Here are some books I recommend related to the topics discussed in this blog.
What kind of pattern it will be if my Bill class initializes itself by creating all these complex object in a defined steps in C'tor ? We could even have overloaded c'tors to achieve different sequences or steps ?
ReplyDelete==================================================
Alternatively if I have an Initialze Method or say Generate Method in Bill Class What kind of Pattern it will be ? Any idea ?
The idea of applying design patterns is to solve a recurring problem. By having construction logic inside different classes we are tightly coupling one or more classes. It is outside the scope of the builder pattern. But ideally all the objects constructed using the builder could have used a factory or an abstract factory to loosely couple the exact types from the builder interface.
DeleteThe intent of the Builder pattern is to decouple the steps of construction of a complex object. Imagine if you have a very complex object with nesting of 3-4 levels of hierarchy and the child objects are instantiated based on the values of the parent object. Having this kind logic inside your constructor can make your construction logic quite heavier. You can avoid this by using a builder who takes up the responsibility of building the right objects.
As for your second question having a Generate method in the Bill class, I don't know if there exists any pattern to that effect.
Hope this answers your questions.