Patterns. Builder pattern. Review and research. Implementation in C++

Builder pattern. Review and research. Implementation in C++

1. Builder pattern. The purpose. General information

The Builder pattern belongs to the generative patterns and is used to generate objects. The need to use the Builder pattern in a program arises when you need to add new features without significantly changing the code. Capabilities are understood as additional transformations that generate the final product (object). In the Builder pattern, the representation of an object is separated from its construction. At the same time, different representations are obtained for a specific construction.

 

2. The structure of Builder pattern. Figure

The following structure of the Builder pattern is most often found in the literature (Figure 1).

The structure of Builder pattern

Figure 1. The structure of Builder pattern

In Figure 1 above, the structure describes the following:

  • the ConcreteBuilder class that generates the Product object is a concrete builder. The class implements the BuildPart() method of the Builder interface. The class also contains a GetResult() method that returns a pointer (reference) to a constructed object (product). In this way, the ConcreteBuilder class creates some representation in the program;
  • interface (abstract class) Builder, which specifies a method or several methods that are provided to the manager (class Director), which calls the process of constructing an object;
  • the Director class – gets a builder reference (pointer) to the Builder interface. This is a typical form of aggregation. Using this reference, the class can invoke an instance of the ConcreteBuilder class. If there are several builder classes (like ConcreteBuilder), then it is possible to choose the appropriate methods for constructing objects of these classes (products);
  • Product is a specific product (object) of the ConcreteBuilder class that is returned by the GetResult() method.

 

3. Client use of the Builder pattern. Interaction diagram

When using the Builder pattern, the client has at its disposal:

  • class of the manager Director;
  • interface (abstract class, class) Builder.

The sequence of steps of the client is as follows.

  1. Create an object of class of type ConcreteBuilder (create a builder).
  2. Create an instance of the Director class and configure it with an instance of the ConcreteBuilder class.
  3. In the Director class, call methods (requests) that call the methods of the ConcreteBuilder class with a request to build a product (object). As a result, the ConcreteBuilder creates an object of type Product.
  4. The client receives the product from the builder and uses it.

Figure 2 shows a diagram of a client’s interaction with a Director and a ConcreteBuilder.

Builder pattern. Interaction diagramm

Figure 2. Builder pattern. Interaction diagramm

 

4. An example of the implementation of the Builder pattern in C++

The example demonstrates the use of the Builder pattern to generate objects of the Product class. Class names correspond to Figure 1. It is conventionally accepted that the Product class contains code that consists of two integer parts: part1, part2. These parts need to be built using the Builder pattern approach. In the client code (the main() function), an instance of the Product class is obtained with the values of parts 25 and 777. Then the obtained result is checked. Then the result is checked.

Figure 3 shows the structure that is used to solve this problem.

Builder pattern. Structure for solving the problem

Figure 3. Builder pattern. Structure for solving the problem

The text of the solution to the problem in C++ (Console Application), which corresponds to Figure 3, is as follows.

#include <iostream>
using namespace std;

// The class that is the product
class Product
{
public:
  int part1; // part 1
  int part2; // part 2
};

// A class that implements an interface with a client
class Builder
{
public:
  virtual void CreateProduct() { }
  virtual void BuildPart1(int part1) { }
  virtual void BuildPart2(int part2) { }
  virtual Product* GetResult() { return nullptr; }
};

// A class that is a concrete builder
class ConcreteBuilder : public Builder
{
private:
  Product* currentBuilder;

public:
  // Constructor
  ConcreteBuilder()
  {
    currentBuilder = nullptr;
  }

  // Implementation of virtual methods
  virtual void CreateProduct()
  {
    cout << "ConcreteBuilder::CreateProduct()" << endl;
    currentBuilder = new Product();
  }

  // Build the part 1
  virtual void BuildPart1(int part1)
  {
    cout << "ConcreteBuilder::currentBuilder->part1 = " << part1 << endl;
    currentBuilder->part1 = part1;
  }

  // Build the part 2
  virtual void BuildPart2(int part2)
  {
    cout << "ConcreteBuilder::currentBuilder->part2 = " << part2 << endl;
    currentBuilder->part2 = part2;
  }

  // Method that returns a product for a client
  virtual Product* GetResult()
  {
    return currentBuilder;
  }

  // Destructor
  ~ConcreteBuilder()
  {
    if (currentBuilder != nullptr)
      delete currentBuilder;
  }
};

// Class - Manager
class Director
{
public:
  // Method that constructs parts
  void Construct(Builder& builder)
  {
    // 1. Create a product
    builder.CreateProduct();

    // 2. Build part 1
    builder.BuildPart1(25);

    // 3. Build part 2
    builder.BuildPart2(777);
  }
};

void main()
{
  // The main() function in this case acts as a client
  // 1. Declare a pointer to the product to be obtained
  Product* product;

  // 2. Create a concrete instance of the ConcreteBuilder class and fill with values
  ConcreteBuilder B;

  // 3. Create a class-manager and configure it the product B
  Director D;
  D.Construct(B); // configure


  // 4. After configuration, pass the created product to the client
  product = B.GetResult();

  // 5. Print the value of the product for the control of
  cout << "product->part1 = " << product->part1 << endl;
  cout << "product->part2 = " << product->part2 << endl;
}

After execution, the program will give the following result

ConcreteBuilder::CreateProduct()
ConcreteBuilder::currentBuilder->part1 = 25
ConcreteBuilder::currentBuilder->part2 = 777
product->part1 = 25
product->part2 = 777

 

5. Features of using the Builder pattern

The following characteristic features of the use can be divided into pattern Builder:

  • the pattern is built in such a way that it allows you to change the internal representation of the product. You can perform different options for constructing the resulting object;
  • the pattern strictly separates the code for constructing an object from the code for representing a complex object;
  • the Builder pattern improves modularity. This means that the way of constructing and representing a complex object is encapsulated. The client is unaware of the implementation specifics of the classes that define the internal structure of the product;
  • it is possible to add new builders (ConcreteBuilder2, ConcreteBuilder3, etc.) without changing the structure of the main code;
  • the Director can build different versions of the product from the same parts. There can also be several managers (Director2, Director3, etc.);
  • the product design process can be broken down into parts (BuildPart1(), BuildPart2(), etc.) under the direction of the Director. Compared to other generative patterns in the Builder pattern, the product design process is more flexible.