생성 패턴 - ② 팩토리 메서드 패턴
객체를 생성하는 일은 다소 번거롭고 많은 주의가 요구됩니다. 만약 생성 절차가 특별한 규칙을 따라야 한다면 객체를 생성하는 일은 더욱 까다로워집니다. 디자인 패턴에서 생성 패턴은 객체 생성의 어려움을 해소하기 위해 등장했습니다. 생성 패턴은 객체를 만드는 복잡한 과정을 추상화한 패턴입니다. 생성 패턴의 종류는 다음과 같습니다.
- Factory Pattern
- Factory Method Pattern
- Abstract Factory Pattern
- Builder Pattern
- Singleton Pattern
- Prototype Pattern
이번 글에서는 두 번째 팩토리 메서드 패턴에 대해서 다뤄보도록 하겠습니다.
Factory Method Pattern
팩토리 메서드는 팩토리의 확장입니다. 팩토리 메서드 패턴은 팩토리 패턴을 활용한 객체 생성 방식에 다양한 기능을 추가할 필요가 있을 때 사용할 수 있습니다. 예제를 통해서 살펴보도록 하겠습니다.
팩토리 메서드 구현
우선 팩토리 인터페이스를 팩토리 인터페이스를 만든다. 이를 상속받아 Cat객체를 생성하는 CatFactory 나 Dog 객체를 생성하는 DogFactory를 만들 수 있습니다. 그리고 이들 객체는 각각 상속받은 CreateAnimal 메서드를 활용하여 Cat 객체와 Dog 객체를 만들 수 있습니다.
class Animal {
public:
virtual void Speak() = 0;
};
class Cat : public Animal {
public:
void Speak() override { cout << "meow~" << endl; }
};
class Dog : public Animal {
public:
void Speak() override { cout << "bark~!" << endl; }
};
class AnimalFactory {
public:
virtual shared_ptr<Animal> CreateAnimal() = 0;
};
class CatFactory : public AnimalFactory {
public:
shared_ptr<Animal> CreateAnimal() override
{
return make_shared<Cat>();
}
};
class DogFactory : public AnimalFactory {
public:
shared_ptr<Animal> CreateAnimal() override
{
return make_shared<Dog>();
}
};
int main()
{
CatFactory cat_factory;
shared_ptr<Cat> kitty = static_pointer_cast<Cat>(cat_factory.CreateAnimal());
kitty->Speak();
DogFactory dog_factory;
shared_ptr<Dog> bingo = static_pointer_cast<Dog>(dog_factory.CreateAnimal());
bingo->Speak();
return 0;
}
이렇게 메서드를 활용하여 객체를 생성화는 예제를 살펴보았습니다. 그러나, 지금까지는 앞서 다룬 팩토리 함수나, 팩토리 객체를 활용하는 것과 결과적으로 차이는 없습니다. 이제 팩토리 메서드의 기능을 조금 더 확장해보겠습니다.
팩토리 메서드의 확장
팩토리 메서드는 기존 팩토리 패턴보다 더욱 다양한 기능들을 추가할 수 있다고 하였습니다. 예를 들어 팩토리 객체가 고양이를 몇 마리 생성했는지 팩토리 객체의 상태를 확인한다거나, 강아지에 날개를 추가하는 등의 기능들을 추가할 수 있습니다.
// Animal, Cat, Dog, AnimalFactory 클래스는 위와 동일
class CatFactory : public AnimalFactory {
public:
int GetCatCount() const {return cat_count; } // 확장 기능
static int cat_count;
shared_ptr<Animal> CreateAnimal() override
{
cat_count++;
return make_shared<Cat>();
}
};
int CatFactory::cat_count = 0;
class DogFactory : public AnimalFactory {
public:
shared_ptr<Animal> CreateAnimal() override
{
return make_shared<Dog>();
}
void MakeWings(shared_ptr<Dog>& dog) // 확장 기능
{
// add wings
cout << "dog wings added" << endl;
}
};
int main()
{
CatFactory cat_factory;
shared_ptr<Cat> kitty = static_pointer_cast<Cat>(cat_factory.CreateAnimal());
kitty->Speak();
cout << cat_factory.GetCatCount() << " cats are created" << endl;
DogFactory dog_factory;
shared_ptr<Dog> bingo = static_pointer_cast<Dog>(dog_factory.CreateAnimal());
bingo->Speak();
dog_factory.MakeWings(bingo);
return 0;
}
실제 팩토리 메서드가 활용될 때는 팩토리 객체는 객체를 생성하는 메서드뿐만 아니라 다양한 기능을 함께 제공할 수도 있습니다. 그래서 팩토리의 이름도 DogFactory라는 명확한 이름보다는 DogManager, CatCreator와 같은 이름을 가지지만 내부에는 객체를 생성하는 팩토리 패턴이 사용된 경우도 있습니다. 이런 경우는 코드를 살펴보면서 오브젝 생성 파트가 클래스별로 상속되어 있다면 팩토리 메서드 패턴이 적용되었다고 생각할 수 있습니다.
<출처>
유튜브 채널 "코드없는 프로그래밍": https://www.youtube.com/channel/UCHcG02L6TSS-StkSbqVy6Fg
길벗 출판사: 모던 C++ 디자인 패턴(Dmitri Nesteruk 저)