In this post we will discuss the Builder Pattern. This is one of the standard creational design patterns.
It is particularly relevant when dealing with complex objects which may be conceptually built up from a set of components. In this pattern, the object is often built up in stages rather than being created immediately in the constructor.
Example : Assembling a computer
By way of example, we will consider the problem of assembling a new computer system. There are of course many online retailers to choose from. Some of these simply offer a list of stock items with fixed configurations. However, some retailers allow you to customise your own system.
Computer systems are often categorised. For example, a store might have separate pages for the following:
- Laptops
- Office PC
- Gaming PC
However, there is commonality to all these systems. For example, they all have a display, keyboard, CPU etc.
We shall imagine we are writing an online ‘Configurator’ so that we can build the right PC for the customer requirements. One possible approach is given by the following UML class diagram:
We have separate classes for building desktop and laptop systems (DesktopBuilder and LaptopBuilder class). So that our configurator can interact with either builder, we have created a generic ISystemBuilder interface.
Whichever builder we use, the end result is a Computer object (which will have the appropriate specifications).
This is how we might implement the pattern in C#:
class Computer
{
public string GraphicsAdapter { get; set; }
public string PointingDevice { get; set; }
}
class SystemConfigurator
{
private ISystemBuilder _builder;
public SystemConfigurator(ISystemBuilder builder)
{
_builder = builder;
}
public void build()
{
_builder.selectGraphicsAdapter();
_builder.selectPointingDevice();
}
}
interface ISystemBuilder
{
void selectGraphicsAdapter();
void selectPointingDevice();
Computer getComputer();
}
class DesktopBuilder : ISystemBuilder
{
private Computer _computer = new Computer();
public void selectGraphicsAdapter()
{
_computer.GraphicsAdapter = "PCI Express graphics adapter";
}
public void selectPointingDevice()
{
_computer.PointingDevice = "Optical USB mouse";
}
public Computer getComputer()
{
return _computer;
}
}
class LaptopBuilder : ISystemBuilder
{
private Computer _computer = new Computer();
public void selectGraphicsAdapter()
{
_computer.GraphicsAdapter = "Onboard graphics";
}
public void selectPointingDevice()
{
_computer.PointingDevice = "Trackpad";
}
public Computer getComputer()
{
return _computer;
}
}
class Program
{
static void Main(string[] args)
{
var builder = new LaptopBuilder();
var configurator = new SystemConfigurator(builder);
configurator.build();
var computer = builder.getComputer();
Console.WriteLine("Graphics adapter : {0}", computer.GraphicsAdapter);
Console.WriteLine("Pointing device : {0}", computer.PointingDevice);
}
}
Generalisation
Of course, this pattern can be applied to a range of problems. If you come across the Builder Pattern in a programming textbook, it will probably be described in terms of a Director class (equivalent to our SystemConfigurator) and Builder classes.
The UML class diagram is shown below: