팩터리 패턴
- 팩터리 메서드는 생성할 타입의 멤버 함수로, 객체러르 생성하여 리턴함 -> 생성자를 대신함
- 패터리는 별도의 클래스로, 목적하는 객체의 생성 방법을 알 수 있음
- 추상 팩터리는 구현 클래스에서 상속받는 추상 클래스이며, 여러 타입의 팩터리를 생성할 때 사용됨
- 팩터리는 캐싱과 같은 메모리 최적화 구현이 가능함 -> pooling이나 sigleton pattern
3.2 팩터리 메서드
- 어떤 객체를 생성할지 이름으로 명확하게 나타낼 수 없는 생성자 대신 객체를 생성해서 리턴하도록 하는 메서드
- 각각의 팩터리 메서드는 static 함수로 표현
struct Point
{
protected:
Point(const float x, const float y)
: x{x}, y{y} {}
public:
static Point NewCatesian(float x, float y)
{
return Point{ x, y };
}
static Point NewPolar(float r, float theta)
return Point{ r*cos(theta), r*sin(theta) };
}
// 다른 멤버들...
};
auto p = Point::NewPolar(3, 4);
3.3 팩터리
- 빌더와 마찬가지로 객체를 생성하는 함수들을 별도의 클래스에 몰아넣을 수 있는데, 그러한 클래스를 팩터리라고 부름
struct Point
{
float x, y;
friend class PointFactory;
private:
Point(float x, float y) : x(x), y(y) {}
};
struct PointFactory
{
static Point NewCatesian(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_pint = PointFactory::NewCatesian(3, 4);
3.4 내부 팩터리
- 내부 팩터리는 생성할 타입의 내부 클래스로서 존재하는 간단한 팩터리
- 내부 팩터리의 장점은 생성할 타입의 내부 클래스이기 때문에 private 멤버들에 자동적으로 자유로운 접근 권한을 가진다는 점
- 내부 팩터리는 팩터리가 생성해야할 클래스가 단 한종류일 때 유용
- 팩터리가 여러 타입을 활용하여 객체를 생성한다면 내부 팩터리 방식은 적합하지 않음
struct Point
{
private:
Point(float x, float y) : x(x), y(y) {}
};
struct PointFactory
{
private:
PointFactory() {}
public:
static Point NewCatesian(float x, float y)
{
return Point{ x, y };
}
static Point NewPolar(float r, float theta)
return Point{ r*cos(theta), r*sin(theta) };
}
}
public:
float x, y;
static PointFactory Factory;
};
auto pp = Point::Factory.NewCatesian(3, 4);
3.5 추상 팩터리
- 여러 종류의 연관된 객체들을 생성해야할 경우가 있으며, 이때 사용되는 패턴은 추상 팩터리
- 다음 예시는 , DrinkFactory라는 추상 팩터리를 두어 사용 가능한 다양한 팩터리들에 대한 참조를 내부에 가짐
//뜨거운 료에 대해 추상화하는 Factory
struct HotFactory
{
virtual unique_ptr<HotDrink> make() const = 0;
}
struct CoffeeFactory : HotDrinkFactory
{
unique_ptr<HotDrink> make() const override
{
return make_unique<Coffee>();
}
}
struct TeaFactory : HotDrinkFactory
{
unique_ptr<HotDrink> make() const override
{
return make_unique<Tea>();
}
}
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<Hot_Drink> make_drink(const string& name)
{
auto drink = hot_factories[name]->make();
drink->prepare(200);
return drink;
}
};
3.6 함수형 팩터리
- 저장된 팩터리를 직접 호출하는 과정을, 함수형 팩터리를 이용해서 생략 가능함
class DrinkWithVolumeFactory
{
map<string, function<unique_ptr<HotDrink>()>> factories;
public:
DrinkWithVolumneFactory()
{
// prepare 하는 절차 제체를 팩터리 내에 내장 가능 -> 함수블록으로 쉽게 수정
factories["tea"] = [] {
auto tea = make_unique<tea>();
tea->prepare(200);
return tea;
};
}
};
// 저장된 팩터리를 직접 호출하는 과정
inline unique_ptr<HotDrink>
DrinkWithVolumeFactory::make_unique<const string& name)
{
return factories[name]();
}
? 팩터리 패턴과 빌더 패턴의 차이점
- 목적 : 팩터리 패턴은 객체 생성 인터페이스를 제공하여 클라이언트가 직접 생성하는 것을 방지함, 빌더 패턴은 복잡한 객체의 생성 과정을 추상화하여 단계별로 구성할 수 있도록 함
- 사용 방법 : 팩터리 패턴은 단순한 객체 생성을 추상화하고 변경 가능성이 낮을 때 사용되며, 빌더 패턴은 복잡한 객체 생성 과정이 단계별로 처리되어야할 때 유용함
- 구조 : 팩터리 패턴은 인터페이스를 정의하고 이를 통해 구체적인 객체를 생성하는 메서드를 제공하며, 빌더 패턴은 빌더 클래스를 사용하여 객체 생성 과정을 단계적으로 처리함
- 유연성 : 팩터리 패턴은 유연성이 낮고 변경이 필요한 경우 인터페이스를 수정해야 하지만, 빌더 패턴은 각 단계를 자유롭게 조절할 수 있어 유연성이 높음
- 적합한 상황 : 팩터리 패턴은 단순한 객체 생성이 필요하고 생성 과정이 자주 변경되지 않을 때, 빌더 패턴은 복잡한 객체 생성이 필요하거나 생성 과정이 자주 변경될 때 적합함
'프로그래밍공부 > design pattern' 카테고리의 다른 글
[모던 c++의 디자인 패턴] 2장. 빌더 (0) | 2024.04.09 |
---|---|
[모던 c++의 디자인 패턴] 1장. SOLID 디자인 원칙 (1) | 2024.03.04 |