If It’s Complex, Let’s Call the Builder: Use and Applicability of the Builder Design Pattern

Oct 7th - 2024
By
Belatrix

What is the Builder Creational Design Pattern and why do we need it?

In real life, a builder constructs complex things and does this in small steps, piece by piece. In software development, the Builder Design Pattern allows us to handle the creation of different types and representations of a rather complex object by using the same creation code. To understand better what is all about, let’s have a look at a real-world inspired sample. The C# 11 source code is available on GitHub.

Discover the Builder Design Pattern, which enables the step-by-step construction of complex objects in software development, using the same creation code for various types and representations, with examples available in C# 11 on GitHub.

How and why the Builder Design Pattern is used: an example of implementation

This time, to keep things simple, the focus will be on just one product, the coffee table, but the same approach can be used to build the other sample products.

The furniture shop application is capable of producing furniture sets in different variants, like Modern, Classic, and Retro. Different products like armchairs, sofas, and coffee tables are available for each variant. Each furniture piece or set is manufactured upon request from a Client.

In the sample for the Abstract Factory, the MakeCoffeeTable() method implementation for each variant is naive, taking some arguments to customize the product’s size and color, this approach being far from reality. Each variant should have a set of predefined dimensional values and/or colors, and the code for the Builder was changed to implement this approach. Additionally, there are specific details about the table plate, handlers, legs (binding components, screws, and rollers), and maybe more.

It’s obvious that things are getting complicated, so why not delegate the job to a specialized component? This is the point at which the builder (or a carpenter if you want) enters the stage.

The standard UML diagram for the Builder Design Pattern looks like this:

Learn about the Builder Design Pattern and its UML diagram, illustrating how to delegate the complexity of constructing objects to a specialized component, enhancing clarity and maintainability in software design.

The Director class’s responsibility is to create an object using the Builder interface. It specifies the order of building steps, while a ConcreteBuilder provides the implementation for those steps. It’s not required for our application to have a Director class. The building steps may be called directly from the Client code in a specific order. However, the Director class might be a suitable location to place various construction routines so we can reuse them across our application. Moreover, the Director class completely hides from the Client code the details of Product construction. The Client only needs to associate a Builder with a Director, have the Director start construction, and have the Builder deliver the finished product.

Builder defines an abstract interface for creating Product object parts (all the required steps).

ConcreteBuilder constructs and assembles parts of the product by implementing the Builder interface which also contains a method for retrieving the product (GetResult()). Of course, we can have more than one ConcreteBuilder, each responsible for creating a specific product.

The Product represents the complex object under construction and includes classes that define the constituent parts.

In our particular case with the furniture shop, we have a deviation from the standard definition. The reason for this custom approach is to emphasize the fact that we should adapt the design patterns to our particular needs, instead of forcing our implementation to fit into a pattern blueprint. Moreover, we want to improve our existing implementation based on Abstract Factory and that’s another reason for this deviation.

Explore how the Builder Design Pattern can be adapted for a furniture shop scenario, emphasizing the importance of tailoring design patterns to fit unique requirements and enhancing existing implementations based on the Abstract Factory pattern.

In our particular example, the RetroFurnitureFactory acts as the Director; its implementation of the MakeCoffeeTable() – the equivalent of the Costruct() – instantiates the CoffeeTableBuilder class. In the standard definition of the pattern, the Director is supposed to receive the instance of the ConcreteBuilder via Dependency Injection, but in our implementation, we have a particular factory (responsible for a product variant and a method for a CoffeeTable), so it is a good idea to use the above-mentioned approach. Moreover, the sample implementation is relying on .Net Generics, to provide the required abstraction for the CoffeeTable building.

CoffeeTableBuilder provides the implementation of the “steps”: BuildTableLegs(), BuildTablePlate(), and BuildTableHandles(). GetResult() method is responsible to provide an instance of the RetroCoffeeTable object (in this particular case) to the Client.

For other product variants like Modern and Classic, we have a similar implementation in the sample code.

In this way, we managed to simplify the code from the previous implementation and to provide support for a flexible construction of the “products”.

The essence of it: advantages and disadvantages of the Builder Design Pattern

We should use the Builder Design Pattern when we want:

  • To get rid of the multiple constructors of a class, where one differs from the others just by the number of parameters.
  • As long as this pattern allows us to build objects step by step, using only those steps that we need, we can simplify our code by reducing the number of these constructors, or, much better, by getting rid of them entirely.
  • Our code to create different representations of some Product (different variants of coffee tables, armchairs, and sofas).
  • To create composite object hierarchies or other complex objects.

There are some disadvantages, like:

  • Although there are at least twice as many lines of code, the effort pays off in terms of design flexibility and code readability.
  • Requires developing a specific ConcreteBuilder for every different Product type.

While carrying out construction steps, a builder should not expose the unfinished product. This prevents the client code from fetching an incomplete result.

That was for the Builder Design Pattern. The C# source code for the presented sample can be found on GitHub.

Oct 7th - 2024
More Stories for you
Discover the evolution of Java since its inception in 1995, highlighting the diverse flavors of Java versions, JDK distributions, and optimizations that enhance its competitiveness in modern cloud-native environments.

Get Ready for the New Decade of Java Software Development:...

Oct 7th - 2024
Back in 1995, a team of engineers from Sun Microsystems was trying to come up with a name for their new software platform and programming language. In a brainstorming session...
Discover how Claude Instant 1.2 represents a significant advancement in AI technology, enhancing human-machine collaboration and transforming our interactions with AI systems for a more seamless experience.

Claude Instant 1.2: Unveiling a Safer, Better, Faster, and Stronger...

Oct 7th - 2024
Anthropic has recently released the latest version of its AI model, Claude Instant 1.2, which is now available through their API. This new version incorporates the strengths of their flagship...