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