The builder pattern of the design pattern C and python implementation .

Mondo Technology Updated on 2024-01-19

The builder pattern, also known as the builder pattern, is an object-built design pattern. It is used to create complex objects, separating the construction of a complex object from the representation, so that the same construction process can create different representations. It consists of four roles: Product, Builder, ConcreteBuilder, and Director.

Imagine a complex object that requires laborious, step-by-step initialization of many fields and nested objects. This type of initialization is often hidden in a huge constructor with a large number of parameters.

For example, let's consider how to create a house object. To build a simple house, you need to build four walls and a floor, install a door, install a pair of windows, and then build a roof. But what if you want a bigger, brighter house with a backyard and other amenities like heating, plumbing, and wiring?

The simplest solution is to extend the house base class and create a set of subclasses to cover all combinations of parameters. But in the end you'll get quite a few subclasses. Any new parameters, such as the porch style, need to further develop this hierarchy.

There is another way that doesn't involve creating subclasses. You can create a huge constructor in the house base class that contains all the possible parameters that control the house object. While this approach does eliminate the need for subclasses, it creates another problem. In most cases, most arguments are not used, which makes constructor calls very ugly. For example, only a small percentage of houses have swimming pools, so the parameters associated with swimming pools are useless.

Builder mode recommends that you take an object from its own class to build and move it to a separate object called the builder.

This pattern organizes object construction into a set of steps (buildwalls, builddoor, and so on). To create an object, you need to perform a series of these steps on the builder object. The important thing is that you don't need to call all the steps. You can only invoke those steps that are required for a specific configuration of the resulting object. When you need to build various representations of a product, some build steps may require different implementations. For example, the walls of a cottage can be built of wood, but the walls of a castle must be made of stone.

In this case, you can create multiple different builder classes that implement the same set of build steps in different ways. You can then use these builders to generate different types of objects during the build process, which is an ordered set of calls to build steps.

Specifically, it consists of four roles: Product, Builder, ConcreteBuilder, and Director.

builder

The builder interface declares product build steps that are common to all types of builders.

concretebuilder

Concrete Builders provides different implementations of the same build step.

Director

Extract a series of calls to the builder steps used to build the product into a separate class called director. The Director class defines the order in which the build steps are executed, while the builder provides the implementation of those steps.

The client must associate one of the builder objects with the director. Usually, it only needs to be done once via the arguments of the director constructor. The director then uses that builder object for all further builds.

it makes sense to use the builder pattern only when your products are quite

complex and require extensive configuration.

unlike in other creational patterns, different concrete builders can produce

unrelated products. in other words, results of various builders may not

always follow the same interface.

class product1else

std::cout <

the builder interface specifies methods for creating the different parts of

the product objects.

class builder

virtual void produceparta() const =0;

virtual void producepartb() const =0;

virtual void producepartc() const =0;

the concrete builder classes follow the builder interface and provide

specific implementations of the building steps. your program may h**e several

variations of builders, implemented differently.

class concretebuilder1 : public builder

concretebuilder1()

void reset()

all production steps work with the same product instance.

void produceparta()const override

void producepartb()const override

void producepartc()const override

concrete builders are supposed to provide their own methods for

retrieving results. that's because various types of builders may create

entirely different products that don't follow the same interface.

therefore, such methods cannot be declared in the base builder interface

at least in a statically typed programming language). note that php is a

dynamically typed language and this method can be in the base interface.

however, we won't declare it there for the sake of clarity.

usually, after returning the end result to the client, a builder instance

is expected to be ready to start producing another product. that's why

it's a usual practice to call the reset method at the end of the

getproduct` method body. however, this beh**ior is not mandatory, and

you can make your builders wait for an explicit reset call from the

client code before disposing of the previous result.

please be careful here with the memory ownership. once you call

getproduct the user of this function is responsable to release this

memory. here could be a better option to use smart pointers to **oid

memory leaks

product1* getproduct()

the director is only responsible for executing the building steps in a

particular sequence. it is helpful when producing products according to a

specific order or configuration. strictly speaking, the director class is

optional, since the client can control builders directly.

class director

the director can construct several product variations using the same

building steps.

void buildminimalviableproduct()

void buildfullfeaturedproduct()

the client code creates a builder object, passes it to the director and then

initiates the construction process. the end result is retrieved from the

builder object.

i used raw pointers for simplicity however you may prefer to use smart

pointers here

void clientcode(director& director)

concretebuilder1* builder = new concretebuilder1();

director.set_builder(builder);

std::cout <

director.buildminimalviableproduct();

product1* p= builder->getproduct();

p->listparts();

delete p;

std::cout <

director.buildfullfeaturedproduct();

p= builder->getproduct();

p->listparts();

delete p;

remember, the builder pattern can be used without a director class.

std::cout <

builder->produceparta();

builder->producepartc();

p=builder->getproduct();

p->listparts();

delete p;

delete builder;

int main()", end="")

class director:

the director is only responsible for executing the building steps in a

particular sequence. it is helpful when producing products according to a

specific order or configuration. strictly speaking, the director class is

optional, since the client can control builders directly.

def __init__(self) -none:

self._builder = none

property

def builder(self) -builder:

return self._builder

builder.setter

def builder(self, builder: builder) -none:

the director works with any builder instance that the client code passes

to it. this way, the client code may alter the final type of the newly

assembled product.

self._builder = builder

the director can construct several product variations using the same

building steps.

def build_minimal_viable_product(self) -none:

self.builder.produce_part_a()

def build_full_featured_product(self) -none:

self.builder.produce_part_a()

self.builder.produce_part_b()

self.builder.produce_part_c()

if __name__ == "__main__":

the client code creates a builder object, passes it to the director and then

initiates the construction process. the end result is retrieved from the

builder object.

director = director()

builder = concretebuilder1()

director.builder = builder

print("standard basic product: ")

director.build_minimal_viable_product()

builder.product.list_parts()

print("")

print("standard full featured product: ")

director.build_full_featured_product()

builder.product.list_parts()

print("")

# remember, the builder pattern can be used without a director class.

print("custom product: ")

builder.produce_part_a()

builder.produce_part_b()

builder.product.list_parts()

The objects that need to be generated have complex internal structures.

The builder pattern encapsulates the specific generation process and details, so that the object can be generated without knowing all the details.

The internal properties of the objects that need to be generated depend on each other.

The builder pattern forces the user to generate objects through a specific process.

Complex objects that want fine-grained control over the internal state during construction.

For example, build a complex, multi-layered nested XML document or HTML.

Better encapsulation.

The user doesn't have to know the implementation details inside the product.

Better scalability.

The builder is completely independent, and the system can extend its precision.

Flexibility. The customer can control the product creation process, the director uses the builder interface, and the replacement or expansion of the builder has little impact on the director class.

This causes the number of classes in the system to increase.

The builder pattern abstracts parts of the system into new classes, so the number of system classes increases.

Increased complexity of the system: multiple new classes were introduced, and there was bound to be a deeper dependency between the commander and the concrete builder.

Related Pages