Modern C++ Design Pattern/Chatper 2. 빌더
created : 2020-04-07T11:44:17+00:00
modified : 2020-09-26T14:10:55+00:00
Builder Pattern
Simple Builder
struct HtmlBuilder
{
HtmlElement root;
HtmlBuilder(string root_name) { root.name = root_name; }
void add_child(string child_name string child_text)
{
HtmlElement e{ child_name, child_text };
root.elements.emplace_back(e);
}
string str() { return root.str(); }
};
Fluent Builder
struct HtmlBuilder
{
HtmlElement root;
HtmlBuilder(string root_name) { root.name = root_name; }
HtmlBuilder& add_child(string child_name string child_text)
{
HtmlElement e{ child_name child_text };
root.elements.emplace_back(e);
return *this;
}
/* skip */
};
HtmlBuilder builder{ "ul" };
builder.add_child("li", "Hello").add_child("li", "world");
cout << builder.str() << endl;
- Fluence Builder Method can return whatever in reference(&) or pointer (*). Just do what you want.
Force to use Builder Class
- delete public constructor
struct HtmlElement { string name; string text; vector<HtmlElement> elements; const size_t indent_size = 2; static unique_ptr<HtmlBuilder> build(const string& root_name) { return make_unique<HtmlBuilder>(root_name); } protected: HtmlElement() {} HtmlElement(const string& name, const string &text) : name{name}, text{text} {} }; struct HtmlBuilder { operator HtmlElement() const { return root; } HtmlElement root; /* skip */ }; /* Can use move semantic */ HtmlElement HtmlBuilder::build() const { return root; }
Groovy
- style builder
struct Tag
{
string name;
string text;
vector<Tag> children;
vector<pair<string, string> > sttributes;
friend ostream& operator << (std::ostream& os, const Tag& tag)
{
/* skip implementation */
}
protected:
Tag(const string& name, const string& text)
: name {name}, text{text} {}
Tag(const string &name, const vector<Tag>& children)
: name{name}, children{children} {}
};
struct P : Tag
{
explicit P(const string& text)
: Tag{"p", text} {}
P(initializer_list<Tag> children)
: Tag{"p", children} {}
};
struct IMG : Tag
{
explicit IMG(const string& url
: Tag{"img", ""}
{
attributes.emplace_back({"src", url});
}
};
std::cout <<
P {
IMG { "http://pokemon.com/pikachu.png" }
}
<< std::endl;
Composite Builder
class PersonBuilderBase
{
protected:
Person& person;
explicit PersonBuilderBase(Person& person)
: person{ person } {}
public:
operator Person()
{
return std::move(person);
}
PersonAddressBuilder lives() const;
PersonJobBuilder works() const;
};
class PersonBiulder : public PersonBuilderBase
{
Person p;
public:
PersonBuilder() : PersonBuilderBase{p} {}
};
class PersonAddressBuilder : public PersonBuilderBase
{
typedef PersonAddressBuilder self;
public:
explicit PersonAddressBuilder(Person& person)
: PersonBuilderBase { person } {}
self& at(std::string street_address)
{
person.street_address = street_address;
return *this;
}
self& with_postcode(std::string post_code) { /* skip */ }
self& in(std::string city) { /* skip */ }
};
Person p = Person::create()
.lives().at("123 London Road")
.with_postcode("SW1 1GB")
.in("London")
.works().at("PragmaSoft")
.as_a("Consultant")
.earning(10e6);
Summary
- The purpose of
Builder Pattern
is to create a complicate object, which needs a combination of many items. - Properties of Builder Pattern
- If you use the influence interface builder, just need only one call chain. Of course, the builder methods are constructed to return
this
(pointer) or*this
(reference). - Hide public outter constructor of target object to force using builder class or method. Also can define good operators to achieve this.
- Awesome
groovy
-style builder can be constructed with uniform initialiing syntax. - A Builder interface can expose other sub builders. (refer to composite builder)
- If you use the influence interface builder, just need only one call chain. Of course, the builder methods are constructed to return