Skip to content

Commit ba08fc4

Browse files
authored
Merge pull request #10 from M1TE5H/main
RealWorld Builder Example
2 parents a41c5c7 + b8a6751 commit ba08fc4

File tree

3 files changed

+180
-2
lines changed

3 files changed

+180
-2
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ This repository is part of the [Refactoring.Guru](https://refactoring.guru/desig
55
It contains C++ examples for all classic GoF design patterns. Each pattern includes two examples:
66

77
- [x] **Conceptual** examples show the internal structure of patterns with detailed comments.
8-
- [ ] **RealWold** examples show how the patterns can be used in a real-world C++ application.
8+
- [ ] **RealWorld** examples show how the patterns can be used in a real-world C++ application.
99

1010

1111
## Requirements
@@ -43,7 +43,7 @@ Here's a style guide which might help you to keep your changes consistent with t
4343

4444
1. All code should match the [Google style guide].
4545
2. Aim to put all code within one .cc file. Yes, I realize that it's not how it supposed to be done in production. However, it helps people to understand examples better, since all code fits into one screen.
46-
3. The comments doesn't follow the style guide for compatibility reasons withe other language examples.
46+
3. The comments doesn't follow the style guide for compatibility reasons with other language examples.
4747

4848

4949

src/Builder/RealWorld/Output.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<body>
2+
<h1>Title of the Page</h1>
3+
<h2>Subtitle A</h2>
4+
<p>Lorem ipsum dolor sit amet, ...</p>
5+
<h2>Subtitle B</h2>
6+
<p>... consectetur adipiscing elit.</p>
7+
</body>

src/Builder/RealWorld/main.cc

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#include <iostream>
2+
#include <string>
3+
#include <vector>
4+
5+
/**
6+
* EN: Real World Example for the Builder Design Pattern (C++03/11 Evolution)
7+
*
8+
* Need: Consider a representation of the Document Object Model in which
9+
* each HTML element is a non-trivial graph (multi-way tree) structure
10+
* whose construction is complicated by the need to add an arbitrary number of
11+
* children to the root.
12+
*
13+
* Solution: A HTML ElementBuilder can be used for stepwise construction of an
14+
* Element using an implementational variant of the Builder Design Pattern
15+
* known as the \e Fluent Builder. Although modern C++17/20/23 provides the
16+
* neccesary built-in language mechanics (i.e. initializer_list and parameter
17+
* packs) for a Builder, this specific \e Fluent Builder is a class that can be
18+
* applied to legacy code relying on the older C++03/11 standards.
19+
*/
20+
21+
#include <iostream>
22+
#include <string>
23+
#include <vector>
24+
25+
/**
26+
* EN: These preprocessor directives allow this standalone code to target both
27+
* pre- and post-C++11 standards when it comes to std::vector, in particular,
28+
* appending a new element as well as iterating over all of the elements. In
29+
* addition, unscoped C++03 and scoped C++11 enums are also handled using the
30+
* same technique. However, this approach is for only demonstration purposes in
31+
* order to show the subtle difference in the C++03- and C++11-subvariants of
32+
* the Fluent Builder as part of the evolution of the design pattern itself.
33+
*/
34+
#if (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L) || \
35+
((!defined(_MSVC_LANG)) && __cplusplus >= 201103L)
36+
37+
#define append_element(tag, content) emplace_back(tag, content)
38+
#define ranged_for(children) for (auto const &child : children)
39+
40+
#define ENUMMERATION_TYPE() enum class
41+
#define TAG_SCOPE() html::Tag
42+
43+
#else
44+
45+
#define append_element(tag, content) push_back(Element(tag, content))
46+
#define ranged_for(children) \
47+
for (std::vector<Element>::const_iterator it = children.begin(); \
48+
it != children.end(); ++it)
49+
#define child *it
50+
51+
#define ENUMMERATION_TYPE() enum
52+
#define TAG_SCOPE() html
53+
54+
#endif
55+
56+
/**
57+
* EN: The html namespace contains the core machinery of the Fluent Builder
58+
* Pattern, namely, the Element and ElementBuilder classes. To showcase the
59+
* versatility of the pattern in being able to extend the Element class with
60+
* different types of HTML elements (tags), a print method that relies on
61+
* various tags is provided to show the Fluent Builder in action.
62+
*/
63+
namespace html {
64+
65+
/**
66+
* EN: The forward declaration for the ElementBuilder is necessary as it is
67+
* a friend class of the Element class in this Fluent Builder implementation.
68+
*/
69+
class ElementBuilder;
70+
71+
/**
72+
* EN: Enumeration to represent different HTML elements. (Note that in C++11
73+
* the enumeration will be class-scoped.) There is also a naive helper function
74+
* to convert the names into strings, which is used inside of the print method.
75+
*/
76+
ENUMMERATION_TYPE() Tag{body, h1, h2, p, /* ... */};
77+
78+
std::string to_string(Tag tag) {
79+
switch (tag) {
80+
case TAG_SCOPE()::body:
81+
return "body";
82+
case TAG_SCOPE()::h1:
83+
return "h1";
84+
case TAG_SCOPE()::h2:
85+
return "h2";
86+
case TAG_SCOPE()::p:
87+
return "p";
88+
/* ... */
89+
default:
90+
return "tag";
91+
}
92+
}
93+
94+
/**
95+
* EN: This client-facing Element class is essentially a tree node that
96+
* stores its children by value in a dynamic container. The Fluent Builder
97+
* provides a means to construct an instance of root Element node and then add
98+
* an arbitrary number of children Element nodes.
99+
*/
100+
class Element {
101+
public:
102+
Element(Tag tag, std::string const &content = std::string())
103+
: tag_(tag), content_(content) {}
104+
105+
/**
106+
* EN: The print method generates markup. Note that the ranged-based for
107+
* loop over the children differs between the respective C++03 and C++11
108+
* standards.
109+
*/
110+
friend std::ostream &operator<<(std::ostream &os, Element const &e) {
111+
os << "<" << to_string(e.tag_) << ">";
112+
if (!e.content_.empty()) {
113+
os << e.content_;
114+
} else {
115+
os << "\n";
116+
}
117+
ranged_for(e.children_) { os << child; }
118+
os << "</" << to_string(e.tag_) << ">\n";
119+
return os;
120+
}
121+
122+
private:
123+
friend class ElementBuilder;
124+
125+
private:
126+
Tag tag_;
127+
std::string content_;
128+
std::vector<Element> children_;
129+
};
130+
131+
/**
132+
* EN: The Fluent Builder is named for its method chaining as the modifier
133+
* (setter) method add_child() returns the builder itself, and so it can be
134+
* repeatedly called to construct a complex Element with many Element children.
135+
*
136+
* Again note that that element addition operation on the vector of children
137+
* differs between the C++03 and C++11 standards; in the former case, the
138+
* Element constructor must be called explicitly whereas in the latter case, the
139+
* arguments are forwarded to the Element constructor.
140+
*/
141+
class ElementBuilder {
142+
public:
143+
explicit ElementBuilder(Tag tag, std::string const &content = std::string())
144+
: root_(Element(tag, content)) {}
145+
146+
ElementBuilder &add_child(Tag tag,
147+
std::string const &content = std::string()) {
148+
root_.children_.append_element(tag, content);
149+
return *this;
150+
}
151+
152+
operator Element() const { return root_; }
153+
154+
private:
155+
Element root_;
156+
};
157+
158+
} // namespace html
159+
160+
int main() {
161+
html::Element body =
162+
html::ElementBuilder(TAG_SCOPE()::body)
163+
.add_child(TAG_SCOPE()::h1, "Title of the Page")
164+
.add_child(TAG_SCOPE()::h2, "Subtitle A")
165+
.add_child(TAG_SCOPE()::p, "Lorem ipsum dolor sit amet, ...")
166+
.add_child(TAG_SCOPE()::h2, "Subtitle B")
167+
.add_child(TAG_SCOPE()::p, "... consectetur adipiscing elit.")
168+
/* ... */;
169+
170+
std::cout << body;
171+
}

0 commit comments

Comments
 (0)