모던 c++ 디자인 패턴 을 읽고 인상 깊었던 내용을 정리합니다.
class Person
{
// 주소
std::string street_address, post_code, city;
// 직업
std::string company_name, position;
int annual_income = 0;
Person() {}
};
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 PersonBuilder : 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) {...}
self& in(std::string city) {...}
};
// PersonJobBuilder도 같은 방식으로 구현된다.
Person p = Person::create() //빌더를 얻고,
.lives() //PersonAddressBuilder를 얻는다.
.at("123 London Road") //주소 정보 설정
.with_postcode("SW1 1GB") //..
.in("London") //..
.works() //PersonJobBuilder를 얻는다.
.at("PragmaSoft") //직업 정보 설정
.as_a("Consultant") //..
.earing(10e6); //..
어떤 객체를 생성하는지를 이름으로 명확하게 나타낼 수 없는 생성자 대신 객체를 생성하여 리턴하도록 하는 팩터리 메서드를 구현할 수 있다.
빌더와 마찬가지로 Point를 생성하는 함수들을 별도의 클래스에 몰아넣을 수 있다. 그러한 클래스를 팩터리라고 부른다.
struct Point
{
float x, y;
friend class PointFactory;
private:
Point(float x, float y) : x(x), y(y) {}
};
struct PointFactory
{
static Point NewCartesian(float x, float y)
{
return Point{ x, y };
}
static Point NewPolar(float r, float theta)
{
return Point{ r*cos(theta), r*sin(theta) };
}
};
auto my_point = PointFactory::NewCartesian(3, 4);
여러 종류의 연관된 객체들을 생성해야 할 경우도 있다. 추상 팩터리는 그러한 경우를 위한 패턴이다.
class DrinkFactory
{
map<string, unique_ptr<HotDrinkFactory>> hot_factories;
public:
DrinkFactory()
{
hot_factories["coffee"] = make_unique<CoffeeFactory>();
hot_factories["tea"] = make_unique<TeaFactory>();
}
unique_ptr<HotDrink> make_drink(const string& name)
{
auto drink = hot_factories[name]->make();
drink->prepare(200);
return drink;
}
};
class DrinkWithVolumeFactory
{
map<string, function<unique_ptr<HotDrink>()>> factories;
public:
DrinkWithVolumeFactory()
{
factories["tea"] = [] {
auto tea = make_unique<Tea>();
tea->prepare(200);
return tea;
};
}
};
// 저장된 팩터리를 직접 호출하는 과정을 다음과 같이 생략할 수 있다.
inline unique_ptr<HotDrink>
DrinkWithVolumeFactory::make_drink(const string& name)
{
return factories[name]();
}