A pragmatic approach to the Builder design pattern, with modern C++
Size: 343.98 KB
Language: en
Added: Jan 02, 2023
Slides: 13 pages
Slide Content
Builder pattern
A pragmatic approach
Builder pattern - Why?
●Large number of attributes needed for
class initialization
○Most of them optional
○"Small" differences among multiple
instances
●Class construction becomes
troublesome
○Too many arguments or too many
constructors
○Awkward to pass unnecessary
arguments
●Wrapping all possible attributes in a
separate class and injecting it, can be
less expressive and inconvenient
Source: refactoring.guru/design-patterns/builder
Builder pattern - How?
●Split initialization in steps
●Initialize relevant attributes only
●Last step returns "complete" instance
●Optional "Director" class to
encapsulate the build steps further
Source: refactoring.guru/design-patterns/builder
Builder pattern - A pragmatic approach
●Textbook examples too complex
●Keep things as simple as possible
●Modern C++
●Skip the "Director"
○Overkill in most cases
●Demonstrate the simplest Builder, then
move on to more "complex"
○All samples are practical
Good sources for further reading:
●refactoring.guru
●Vishal Chovatiya
⚠ Disclaimers ⚠
-Treat code as pseudocode
-Assuming C++20
-Not (m)any C++20-specific features
demonstrated
-Most, if not everything, should be C++11
compatible
-Not following any specific coding
guidelines
-Assuming no domain restrictions
-There may be room for optimization
Use case: Menu
●Compulsory attributes
○ID
●Optional attributes
○Title
○Border
○Options
○Orientation
Without the Builder
// Constructor with all fields
Menu(std::string id,
std::string title,
std::vector<std::string> options,
bool horizontal,
int border);
// Constructors with all field combinations
Menu(std::string id);
Menu(std::string id, std::string title);
Menu(std::string id, std::vector<std::string> options);
// ...
Menu(std::string id);
void setTitle(std::string title);
void setOptions(std::vector<std::string> options);
void setHorizontal(bool horizontal);
void setBorder(int border);
Basic Builder
class Menu {
public:
static Menu create(std::string id) { return Menu{id}; }
void show() const {}
void select(int /* index */) const {}
private:
std::unique_ptr<DefaultMenu> mMenu{};
};
class DefaultMenu : public Menu {
public:
void show() const override {}
void select(int /* index */) const override {}
private:
friend class MenuBuilder;
std::string mId{};
std::string mTitle{};
std::vector<std::string> mOpts{};
bool mHorizontal{false};
int mBorder{0};
DefaultMenu(std::string id) : mId{id} {}
};
Takeaways - Builder pattern
●Facilitate initialization of complex objects
○Simpler
○More expressive
●Don't over-engineer
○Use the simplest form of the pattern that
makes sense
○Don't apply the pattern unless there's the
potential for a large number (>5) of optional
arguments
GitHub: platisd/cpp-builder-pattern
Source: refactoring.guru