Builder_Pattern_2 - 8BitsCoding/RobotMentor GitHub Wiki
라는 문자열을 입력으로 넣고싶다.
- hello
- world
아래와 같이 매번 긴 코드를 입력해야할까?
int demo()
{
// <p>hello</p>
auto text = "hello";
string output;
output += "<p>";
output += text;
output += "</p>";
printf("<p>%s</p>", text);
// <ul><li>hello</li><li>world</li></ul>
string words[] = { "hello", "world" };
ostringstream oss;
oss << "<ul>";
for (auto w : words)
oss << " <li>" << w << "</li>";
oss << "</ul>";
printf(oss.str().c_str());
}예를 들면 아래와 같은식으로?
// easier
HtmlBuilder builder{ "ul" };
builder.add_child("li", "hello").add_child("li", "world");
cout << builder.str() << endl;#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <memory>
using namespace std;
struct HtmlBuilder;
struct HtmlElement
{
string name;
string text;
vector<HtmlElement> elements;
const size_t indent_size = 2;
HtmlElement() {}
HtmlElement(const string& name, const string& text)
: name(name),
text(text)
{
}
string str(int indent = 0) const
{
ostringstream oss;
string i(indent_size*indent, ' ');
oss << i << "<" << name << ">" << endl;
if (text.size() > 0)
oss << string(indent_size*(indent + 1), ' ') << text << endl;
for (const auto& e : elements)
oss << e.str(indent + 1);
oss << i << "</" << name << ">" << endl;
return oss.str();
}
static unique_ptr<HtmlBuilder> build(string root_name)
{
return make_unique<HtmlBuilder>(root_name);
}
};
struct HtmlBuilder
{
HtmlBuilder(string root_name)
{
root.name = root_name;
}
// void to start with
HtmlBuilder& add_child(string child_name, string child_text)
{
HtmlElement e{ child_name, child_text };
root.elements.emplace_back(e);
return *this;
}
// pointer based
HtmlBuilder* add_child_2(string child_name, string child_text)
{
HtmlElement e{ child_name, child_text };
root.elements.emplace_back(e);
return this;
}
string str() { return root.str(); }
operator HtmlElement() const { return root; }
HtmlElement root;
};
int demo()
{
// <p>hello</p>
auto text = "hello";
string output;
output += "<p>";
output += text;
output += "</p>";
printf("<p>%s</p>", text);
// <ul><li>hello</li><li>world</li></ul>
string words[] = { "hello", "world" };
ostringstream oss;
oss << "<ul>";
for (auto w : words)
oss << " <li>" << w << "</li>";
oss << "</ul>";
printf(oss.str().c_str());
// easier
HtmlBuilder builder{ "ul" };
builder.add_child("li", "hello").add_child("li", "world");
cout << builder.str() << endl;
auto builder2 = HtmlElement::build("ul")
->add_child_2("li", "hello")->add_child_2("li", "world");
cout << builder2 << endl;
getchar();
return 0;
}Fluent(유창한) 빌더??
아래와 같이 쭈욱~~~ 빌딩을 하고 싶다는 말
// easier
HtmlBuilder builder{ "ul" };
builder.add_child("li", "hello").add_child("li", "world");
cout << builder.str() << endl;
auto builder2 = HtmlElement::build("ul")->add_child_2("li", "hello")->add_child_2("li", "world");
cout << builder2 << endl;어떻게 구현할까??
// void to start with
HtmlBuilder& add_child(string child_name, string child_text)
{
HtmlElement e{ child_name, child_text };
root.elements.emplace_back(e);
return *this;
}
// pointer based
HtmlBuilder* add_child_2(string child_name, string child_text)
{
HtmlElement e{ child_name, child_text };
root.elements.emplace_back(e);
return this;
}리턴을 주소값으로 주면 된다.
하나 더 주목해야할 점은
static unique_ptr<HtmlBuilder> build(string root_name)
{
return make_unique<HtmlBuilder>(root_name);
}이런식으로 빌드가 가능하다는 점.
하고싶은게 뭐냐?
입력에 주소만 넣으면 아래코드를 만들어주고 싶다.
# 입력 : http://pokemon.com/pikachu.png
<p>
<img src="http://pokemon.com/pikachu.png">
</p>#include <string>
#include <vector>
#include <iostream>
namespace html {
struct Tag
{
std::string name;
std::string text;
std::vector<Tag> children;
std::vector<std::pair<std::string, std::string>> attributes;
friend std::ostream& operator<<(std::ostream& os, const Tag& tag)
{
os << "<" << tag.name;
for (const auto& att : tag.attributes)
os << " " << att.first << "=\"" << att.second << "\"";
if (tag.children.size() == 0 && tag.text.length() == 0)
{
os << "/>" << std::endl;
}
else
{
os << ">" << std::endl;
if (tag.text.length())
os << tag.text << std::endl;
for (const auto& child : tag.children)
os << child;
os << "</" << tag.name << ">" << std::endl;
}
return os;
}
protected:
Tag(const std::string& name, const std::string& text)
: name{name},
text{text}
{
}
Tag(const std::string& name, const std::vector<Tag>& children)
: name{name},
children{children}
{
}
};
struct P : Tag
{
explicit P(const std::string& text)
: Tag{"p", text}
{
}
P(std::initializer_list<Tag> children)
: Tag("p", children)
{
}
};
struct IMG : Tag
{
explicit IMG(const std::string& url)
: Tag{"img", ""}
{
attributes.emplace_back(make_pair("src", url));
}
};
}
int main1()
{
using namespace html;
std::cout <<
P {
IMG {"http://pokemon.com/pikachu.png"}
}
<< std::endl;
getchar();
return 0;
}# 결과
<p>
<img src="http://pokemon.com/pikachu.png">
</p>이제 실전
하고싶은건 Person을 만들고 그 Person은 address와 emploment를 갖는다.
Person을 만드는 Builder를 생성하고 Builder내에서 Address Builder와 Emploment Builder를 별도로 관리하고 싶다.
그리고 그 사용을 아래와 같이 간단하게 하고 싶다면 구현은 어떻게 해야할까??
// Facets.cpp
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <string>
using namespace std;
#include "Person.h"
#include "PersonBuilder.h"
#include "PersonAddressBuilder.h"
#include "PersonJobBuilder.h"
int main__()
{
Person p = Person::create()
.lives().at("123 London Road").with_postcode("SW1 1GB").in("London")
.works().at("PragmaSoft").as_a("Consultant").earning(10e6);
cout << p << endl;
getchar();
return 0;
}// Person.h
#pragma once
#include <string>
#include <iostream>
class PersonBuilder;
class Person
{
// address
std::string street_address, post_code, city;
// employment
std::string company_name, position;
int annual_income = 0;
Person()
{
std::cout << "Person created\n";
}
public:
~Person()
{
std::cout << "Person destroyed\n";
}
static PersonBuilder create();
Person(Person&& other)
: street_address{move(other.street_address)},
post_code{move(other.post_code)},
city{move(other.city)},
company_name{move(other.company_name)},
position{move(other.position)},
annual_income{other.annual_income}
{
}
Person& operator=(Person&& other)
{
if (this == &other)
return *this;
street_address = move(other.street_address);
post_code = move(other.post_code);
city = move(other.city);
company_name = move(other.company_name);
position = move(other.position);
annual_income = other.annual_income;
return *this;
}
friend std::ostream& operator<<(std::ostream& os, const Person& obj)
{
return os
<< "street_address: " << obj.street_address
<< " post_code: " << obj.post_code
<< " city: " << obj.city
<< " company_name: " << obj.company_name
<< " position: " << obj.position
<< " annual_income: " << obj.annual_income;
}
friend class PersonBuilder;
friend class PersonAddressBuilder;
friend class PersonJobBuilder;
};// Person.cpp
#include "Person.h"
#include "PersonBuilder.h"
PersonBuilder Person::create()
{
return PersonBuilder{};
}// PersonBuilder.h
#pragma once
#include "Person.h"
class PersonAddressBuilder;
class PersonJobBuilder;
class PersonBuilderBase
{
protected:
Person& person;
explicit PersonBuilderBase(Person& person)
: person{ person }
{
}
public:
operator Person() const
{
return std::move(person);
}
// builder facets
PersonAddressBuilder lives() const;
PersonJobBuilder works() const;
};
class PersonBuilder : public PersonBuilderBase
{
Person p;
public:
PersonBuilder(): PersonBuilderBase{p}
{
}
};// PersonBuilder.cpp
#include "PersonBuilder.h"
#include "PersonAddressBuilder.h"
#include "PersonJobBuilder.h"
PersonAddressBuilder PersonBuilderBase::lives() const
{
return PersonAddressBuilder{ person };
}
PersonJobBuilder PersonBuilderBase::works() const
{
return PersonJobBuilder{ person };
}// PersonJobBuilder.h
#pragma once
#include "PersonBuilder.h"
class PersonJobBuilder : public PersonBuilderBase
{
typedef PersonJobBuilder Self;
public:
explicit PersonJobBuilder(Person& person)
: PersonBuilderBase { person }
{
}
Self& at(std::string company_name)
{
person.company_name = company_name;
return *this;
}
Self& as_a(std::string position)
{
person.position = position;
return *this;
}
Self& earning(int annual_income)
{
person.annual_income = annual_income;
return *this;
}
};// PersonAddressBuilder.h
#pragma once
#include <string>
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)
{
person.post_code = post_code;
return *this;
}
Self& in(std::string city)
{
person.city = city;
return *this;
}
};