diff --git a/CMakeLists.txt b/CMakeLists.txt index 27acf4e..a8cda02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,6 +110,7 @@ set(APP_SOURCES "src/patterns/behavioral/Observer.cpp" "src/patterns/creational/Singleton.cpp" "src/patterns/creational/FactoryMethod.cpp" + "src/patterns/creational/Builder.cpp" ) # Test files diff --git a/docs/uml/patterns_creational_builder.drawio.svg b/docs/uml/patterns_creational_builder.drawio.svg new file mode 100644 index 0000000..969e0ce --- /dev/null +++ b/docs/uml/patterns_creational_builder.drawio.svg @@ -0,0 +1,4 @@ + + + +
main:
{
 IBuilder* builder = new SimpleBuilder();
 clientCode(builder );
}

clientCode:
{
 Product* product = builder.reset().producePartA()
                           .producePartC()
                           .build()
}

<<Interface>>
IBuilder


+ reset(): IBuilder&
+ producePartA(Type): IBuilder&

+ producePartB(Type)IBuilder&

+ producePartC(Type)IBuilder&


+ build(): IProduct*

Product
- m_parts: vector<type>

+ getParts(): *vector<type>
+ adParts(type): void

<<Abstract>>
AbstractBuilder


# m_product: *Product


+ AbstractBuilder()
+ reset(): IBuilder&
+ build(): IProduct*
SimpleBuilder

+ producePartA(Type): IBuilder&

+ producePartB(Type)IBuilder&

+ producePartC(Type)IBuilder&

ComplexBuilder

+ producePartA(Type): IBuilder&

+ producePartB(Type)IBuilder&

+ producePartC(Type)IBuilder&

Client
+ clientCode(IBuilder* const builder)
1
Extends
Extends
Use
build:
{
 return this->m_product;
}

producePart*:
{
 this->m_product->addPart(*);
 return *this;
}
\ No newline at end of file diff --git a/src/patterns/creational/Builder.cpp b/src/patterns/creational/Builder.cpp new file mode 100644 index 0000000..db86b96 --- /dev/null +++ b/src/patterns/creational/Builder.cpp @@ -0,0 +1,213 @@ +// cppcheck-suppress-file [functionStatic] + +// Builder is a creational design pattern that lets you construct complex objects step by step. +// The pattern allows you to produce different types and representations of an object using the same construction code. +// Appicability: +// (*) Use the Builder pattern to get rid of a “telescoping constructor”. +// (**) when you want your code to be able to create different representations of some product (for example, stone and wooden houses). + +// UML: docs/uml/patterns_behavioral_iterator.drawio.svg + +#include + +#include +#include +#include +#include + +namespace +{ + namespace BuilderPattern + { + class Product + { + private: + std::vector m_parts; + + public: + void addPart(const std::string &part) + { + m_parts.push_back(part); + } + + void print() const + { + std::cout << "Product parts: "; + for (size_t i = 0; i < m_parts.size(); ++i) + { + std::cout << m_parts[i]; + if (i + 1 < m_parts.size()) + std::cout << ", "; + } + std::cout << "\n\n"; + } + }; + + /** + * The Builder interface specifies methods for creating the different parts of + * the Product objects. + */ + class IBuilder + { + public: + virtual ~IBuilder() = default; + virtual IBuilder &reset() = 0; + virtual IBuilder &producePart1() = 0; + virtual IBuilder &producePart2() = 0; + virtual IBuilder &producePart3() = 0; + + virtual Product *const build() = 0; + }; + + class AbstractBuilder : public IBuilder + { + protected: + Product *m_product; + + public: + explicit AbstractBuilder() + { + m_product = new Product(); + } + + ~AbstractBuilder() + { + delete m_product; + } + + AbstractBuilder(const AbstractBuilder &other) + { + if (m_product != nullptr) + { + delete m_product; + } + m_product = new Product(); + *m_product = *other.m_product; + } + + AbstractBuilder &operator=(const AbstractBuilder &other) + { + if (this == &other) + { + return *this; + } + if (m_product != nullptr) + { + delete m_product; + } + m_product = new Product(); + *m_product = *other.m_product; + return *this; + } + + IBuilder &reset() override final + { + if (m_product != nullptr) + { + delete m_product; + } + m_product = new Product(); + + return *this; + } + }; + + /** + * The Concrete Builder classes follow the Builder interface and provide + * specific implementations of the building steps. Your program may have several + * variations of Builders, implemented differently. + */ + class SimpleBuilder : public AbstractBuilder + { + public: + IBuilder &producePart1() override + { + m_product->addPart("PART1"); + return *this; + } + + IBuilder &producePart2() override + { + m_product->addPart("PART2"); + return *this; + } + + IBuilder &producePart3() override + { + m_product->addPart("PART3"); + return *this; + } + + Product *const build() override + { + return m_product; + } + }; + + class ComplexBuilder : public AbstractBuilder + { + public: + IBuilder &producePart1() override + { + m_product->addPart("PART_1-X9a7Fq!2@Lm#48Z"); + return *this; + } + + IBuilder &producePart2() override + { + m_product->addPart("PART_2-X9a7Fq!2@Lm#48Z"); + return *this; + } + + IBuilder &producePart3() override + { + m_product->addPart("PART_3-X9a7Fq!2@Lm#48Z"); + return *this; + } + + Product *const build() override + { + return m_product; + } + }; + + namespace Client + { + void clientCode(IBuilder *const builder) + { + const Product *product1 = (*builder).producePart1().producePart2().producePart3().build(); + product1->print(); + + const Product *product2 = (*builder).reset().producePart1().build(); + product2->print(); + } + } + + void run() + { + { + std::cout << "ConcreteBuilder: Simple\n"; + IBuilder *builder = new SimpleBuilder(); + Client::clientCode(builder); + delete builder; + } + { + std::cout << "ConcreteBuilder: Complex\n"; + IBuilder *builder = new ComplexBuilder(); + Client::clientCode(builder); + delete builder; + } + } + } +} + +struct BuilderAutoRunner +{ + BuilderAutoRunner() + { + std::cout << "\n--- Builder Pattern Example ---\n"; + BuilderPattern::run(); + } +}; + +static BuilderAutoRunner instance; \ No newline at end of file