본문 바로가기
까망 동네/디자인 패턴

팩토리 메서드 패턴 (Factory Method)

by 까망 하르방 2022. 7. 19.
반응형

🎈 팩토리 메서드 패턴(Factory Method Pattern)

• 객체 생성 시 확장을 쉽게 하기 위한 설계 방법

   강력한 결합 관계는 코드의 수정•변경을 어렵게 한다.

 

• 객체 생성 동작을 별도 클래스로 분리하여 처리

    또는 별도의 메서드를 호출하여 객체 생성 동작을 처리

• 객체 생성 처리를 하위(Sub) 클래스로 분리하여 캡슐화하는 패턴

• 새로운 객체를 추가할 때 기존 일반 함수 코드 분석 없이 

    (새로운 하위 클래스 정의) + (Factory 클래스 멤버 함수 추가) 하여 확장 가능

 

• 일반 클래스가 생성자 호출로 직접 객체 생성하지 않고

   Factory(공장)에 위임하는 방식

• 조건에 따라 객체를 다르게 생성하고자 할 때 유용

• 생성 패턴에 해당된다.

 

💻 디자인 패턴(Design Pattern)이란?

👨‍💻 디자인 패턴(Design Pattern)이란? • SW 개발 방법 중에서도 구조적인 문제 해결에 목적을 둔다. • 알고리즘과 같이 특정 문제를 해결하는 Logic 형태보다는    특정 상황에 적용할 수 있

zoosso.tistory.com


 

🎈 예시

• Animal 클래스 존재

    → 해당 객체는 speak() 할 수 있다. (순수가상함수)

Animal를 상속하는 Cat존재

    → 고유 울음소리로 speak() 구현

#include <iostream>
#include <string>

using namespace std;

enum class Ani { CAT };

struct Animal
{
    virtual void speak() = 0;
};

void hello(Animal& animal) {
    animal.speak();
}

struct Cat : public Animal {
    virtual void speak() override {
        cout << "야옹" << endl;
    }
};

void doSth(Ani type)
{
    Animal* pTarget = nullptr;
    if (type == Ani::CAT) {
        pTarget = new Cat();
    }
    
    if (pTarget)
    {
        hello(*pTarget);
        delete pTarget;
    }
}

void main()
{
    doSth(Ani::CAT); // 야옹
}

 

doSth()에서는 new 키워드를 사용하고 있다.

new 키워드는 Cat과 같은 Concrete Class에 의존적이다.

 

 

Q) 개(Dog)를 추가하게 되면 어떻게 될까?

객체 생성을 담당하지 않는 doSth()에서 코드 수정이 필요하다.

이는 OCP에도 위배되는 것으로

doSth() 말고 다른 함수에서도 객체 생성해서

사용하고 있다면 변경작업 규모가 커진다.

 

#include <iostream>
#include <string>

using namespace std;

enum class Ani {CAT, DOG};

struct Animal{
    virtual void speak() = 0;
};

void hello(Animal& animal) {
    animal.speak();
}

struct Cat : public Animal {
    virtual void speak() override {
        cout << "야옹" << endl;
    }
};

struct Dog : public Animal {
    virtual void speak() override {
        cout << "멍멍" << endl;
    }
};

void doSth(Ani type){
    Animal* pTarget = nullptr;
    if (type == Ani::CAT) {
        pTarget = new Cat();
    }
    else if (type == Ani::DOG) { // OCP 위반
        pTarget = new Dog();
    }

    if (pTarget){
        hello(*pTarget);
        delete pTarget;
    }
}

void doSth_Copy(Ani type){
    Animal* pTarget = nullptr;
    if (type == Ani::CAT) {
        pTarget = new Cat();
    }
    else if (type == Ani::DOG) { // OCP 위반
        pTarget = new Dog();
    }

    if (pTarget){
        hello(*pTarget);
        delete pTarget;
    }
}

void main(){
    doSth(Ani::DOG); // 멍멍
}

 

객체 생성을 담당하는 Factory 함수를 만든다.

이제 원숭이, 말, 닭 동물이 추가되더라도 해당 객체를 만들고

Factory 함수에서 관리하면 된다.

 

#include <iostream>
#include <string>

using namespace std;

enum class Ani { CAT, DOG };

struct Animal
{
    virtual void speak() = 0;
};

void hello(Animal& animal) {
    animal.speak();
}

struct Cat : public Animal {
    virtual void speak() override {
        cout << "야옹" << endl;
    }
};

struct Dog : public Animal {
    virtual void speak() override {
        cout << "멍멍" << endl;
    }
};

Animal* Factory(Ani type) {
    switch (type) {
    case Ani::CAT:
        return new Cat();
    case Ani::DOG:
        return new Dog(); // OCP 위반이지만 이정도는 예외로 둔다. 
    }
    throw exception("객체 생성 실패");
}

void doSth(Ani type)
{
    Animal* pTarget = Factory(type);
    if (pTarget) {
        hello(*pTarget);
        delete pTarget;
    }
}

void main()
{
    doSth(Ani::DOG); // 멍멍
}

 

doSth() 함수 입장에서는 이제 인자 하나로 여러 객체를 위임할 수 있게 되었다.

• 다른 일반 함수에서는 해당 Factory를 이용해서 객체 생성하면 된다.

  이는 객체 생성은 Factory가 도맡아 한다고 볼 수 있다.

 

 

Factory Method를 클래스로 제공하는 형태

#include <iostream>
#include <string>

using namespace std;

enum class Ani { CAT, DOG };

struct Animal
{
    virtual void speak() = 0;
};

void hello(Animal& animal) {
    animal.speak();
}

struct Cat : public Animal {
    virtual void speak() override {
        cout << "야옹" << endl;
    }
};

struct Dog : public Animal {
    virtual void speak() override {
        cout << "멍멍" << endl;
    }
};

struct Factory {
    static Animal* create(Ani type) {
        switch (type) {
        case Ani::CAT:
            return new Cat();
        case Ani::DOG:
            return new Dog();
        }
        throw exception("객체 생성 실패");
    }
};

void doSth(Ani type)
{
    Animal* pTarget = Factory::create(type);
    if (pTarget) {
        hello(*pTarget);
        delete pTarget;
    }
}

void main()
{
    doSth(Ani::DOG); // 멍멍
}

 

 

🎈 마치며

• 객체 생성을 Factory (= Sub Class)에게 관리하도록 한다.

• Factory는 객체를 어떻게 생성할지 책임진다.

• 이를 통해 일반 클래스는 객체 생성 과정을 자세히 알 필요 없다.

반응형

댓글