[디자인패턴 with C++] Factory Pattern (추상 팩토리, 함수형 팩토리)

Jin Hur·2022년 5월 8일
0
post-custom-banner

추상 팩토리

팩토리 메서드나 내부 팩토리 방식은 객체 한 종류를 생성하는 경우에 유용하다. 만약 여러 종류의 연관된 객체들을 생성해야 할 경우는 떻게 해야할까? 바로 추상 팩토리 방식을 사용하면 된다.

뜨거운 음료를 판매하는 카페가 있고, 일단 종류가 커피와 차가 있다고 가정한다.

class HotDrink {
public:	
	virtual void prepare(int volume) = 0;
};

class Tea : HotDrink {
public:
	void prepare(int volume) override {
		cout << volume << "양의 차 준비" << endl;
	}
};

class Coffee : HotDrink {
public:
	void prepare(int volume) override {
		cout << volume << "양의 커피 준비" << endl;
	}
};

make_drink라는 함수를 전역에 선언하고, 클라이언트가 사용할 수 있게 한다.

unique_ptr<HotDrink> make_drink(string type) {

	unique_ptr<HotDrink> drink;

	if (type == "Tea" || type == "tea") {
		drink = make_unique<Tea>();
		drink->prepare(200);
	}
	else {
		drink = make_unique<Coffee>();
		drink->prepare(200);
	}
}

이렇게 하나의 함수로 묶으면 차를 만드는 메커니즘과 커피를 만드는 메커니즘이 동시에 묶이기에 SRP를 위반하게 된다. 따라서 클래스 별로 분리한다.

먼저 뜨거운 음료라는 추상 클래스를 만든다.

class HotDrinkFactory {
public:
	virtual unique_ptr<HotDrink> make() const = 0;
};

make()라는 특정 인터페이스를 규정하지만 구현하지는 않았다. 구체적인 팩토리 클래스를 구현하면 다음과 같다.

class TeaFactory : public HotDrinkFactory {
	unique_ptr<HotDrink> make() const override {
		return make_unique<Tea>();
	}
};

class CoffeeFactory : public HotDrinkFactory {
	unique_ptr<HotDrink> make() const override {
		return make_unique<Coffee>();
	}
};

이제 좀 더 상위 수준에서 다른 종류의 음료를 만들 수 있도록(예를 들어 뜨거운 음료 뿐 아니라 차가운 음료까지 만들 수 있도록) DrinkFactory라는 것을 두어 다양한 팩터리들을 참조로 가질 수 있도록 한다.

class DrinkFactory {
private:
	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;
	}
};

문자열("coffee", "tea" ..)을 각 팩토리에 연관시킨 map을 멤버로 가지게 하고, make_drink() 인터페이스를 통해 음료를 팩토리에서 만들게 하고 이를 반환할 수 있게 한다.


함수형 팩토리

함수도 변수에 저장될 수 있다(함수 포인터). 팩토리 객체 포인터를 저장하는 방식이 아닌 함수 포인터를 저장하여 음료를 생성하는 절차 자체를 내장하게 할 수 있다.

class DrinkFactory_withFunc {
private:
	map<string, function<unique_ptr<HotDrink>()>> factories;
public:
	DrinkFactory_withFunc() {
		// 차 생성 함수 포인터 저장
		factories["tea"] = [] {
			auto tea = make_unique<Tea>();
			tea->prepare(200);
			return tea;
		};

		// 커피 생성 함수 포인터 저장
		factories["coffee"] = [] {
			auto coffee = make_unique<Coffee>();
			coffee->prepare(200);
			return coffee;
		};

	}
};

이를 통해 기존 추상 팩토리 형태에서의 make_drink 인터페이스를 아래와 같이 생략할 수 있다.

class DrinkFactory_withFunc {
private:
	map<string, function<unique_ptr<HotDrink>()>> factories;
public:
	...
    unique_ptr<HotDrink> make_drink(const string& name) {
		return factories[name]();
	}
    ...
};
post-custom-banner

0개의 댓글