Design Pattern

factory method

sseram 2022. 6. 7. 23:57
반응형

- Creational Pattern

 

 

에서 factory class로 활용하는 것 까지.

 

 

 

#include <iostream>
#include <vector>

class IScdOperator
{
public:
	virtual void doOperation() = 0;
	virtual ~IScdOperator() {}
};

class StartOperator : public IScdOperator
{
public:
	void doOperation() override { std::cout << "Start Operator!" << std::endl; }
};
class StopOperator : public IScdOperator
{
public:
	void doOperation() override { std::cout << "Stop Operator!" << std::endl; }
};

class ResetOperator : public IScdOperator
{
public:
	void doOperation() override { std::cout << "Reset Operator!" << std::endl; }
};

int main()
{
    IScdOperator* CustomOpr;

	while (1)
	{
		std::string cmd;
		std::cin >> cmd;

        if (cmd == "START_OPR"){
            CustomOpr = new StartOperator();
        }
        else if (cmd == "STOP_OPR"){
            CustomOpr = new StopOperator();
        }
        else if (cmd == "RESET_OPR"){
            CustomOpr = new ResetOperator();
        }
		else{
			std::cout << "Invalid Cmd!" << std::endl;
			continue;
		}

		CustomOpr->doOperation();
	}
}

 

 

이런 식의 코드가 존재한다고 하자.  cmd를 사용자로부터 입력받고, cmd 값에 따라 해야 할 Operation이 달라진다.

Operator가 많아지면 많아질수록 점점 if문이 길어진다.

 

operation이 추가 될 때마다 main을 수정해야하니 적합하지 않다. 저 new 하는 부분을 따로 class로 빼 보자.

 

이런 식의 코드가 존재한다고 하자. cmd를 사용자로부터 입력 받고, cmd의 값에 따라 해야 할 doOperation이 달라진다.
Operator가 많아지면 많아질 수록 점점 if문이 길어진다.
 
 
 
 
 
#include <iostream>
#include <vector>
#include <functional>

class IScdOperator
{
public:
	virtual void doOperation() = 0;
	virtual ~IScdOperator() {}
};

class StartOperator : public IScdOperator
{
public:
	void doOperation() override { std::cout << "Start Operator!" << std::endl; }
};
class StopOperator : public IScdOperator
{
public:
	void doOperation() override { std::cout << "Stop Operator!" << std::endl; }
};

class ResetOperator : public IScdOperator
{
public:
	void doOperation() override { std::cout << "Reset Operator!" << std::endl; }
};

class ScdOprFactory{
private:
    ScdOprFactory(){}
    ~ScdOprFactory(){}
public:
    static ScdOprFactory& getInstance(){
        static ScdOprFactory obj;
        return obj;
    }

    IScdOperator* Create(std::string cmd){

        IScdOperator* p = nullptr;
        if (cmd == "START_OPR"){
            p = new StartOperator();
        }
        else if (cmd == "STOP_OPR"){
            p = new StopOperator();
        }
        else if (cmd == "RESET_OPR"){
            p = new ResetOperator();
        }

        return p;
    }
};

int main()
{
    IScdOperator* CustomOpr;
    ScdOprFactory& factory = ScdOprFactory::getInstance();

	while (1)
	{
		std::string cmd;
		std::cin >> cmd;

        CustomOpr = factory.Create(cmd);

        if (CustomOpr != nullptr)
		    CustomOpr->doOperation();
        else
            std::cout << "Invalid Cmd!" << std::endl;
	}
}
 
 

이런 식으로 분리하면 main 부분은 깔끔하게 유지할 수 있고, ScdOprFactory 부분만 변경하면 된다. 

 

사실 여기서 팩토리 메서드는 끝나지만...

https://ko.wikipedia.org/wiki/%ED%8C%A9%ED%86%A0%EB%A6%AC_%EB%A9%94%EC%84%9C%EB%93%9C_%ED%8C%A8%ED%84%B4

 

팩토리 메서드 패턴 - 위키백과, 우리 모두의 백과사전

 

ko.wikipedia.org

 

 

 

조금 더 가보자.

 

만약 SetupOperator가 추가되었다고 할 때, ScdOprFactory 부분도 수정하여야 한다.

operator가 추가 될 때마다 Factory class의 if문이 점점 길어지므로.... 이걸 바꾸어보자.

 

1. 각 Operator Class에서 new 대신 static Create함수를 만들고,

2. Factory class에서 이 Create 함수를 cmd와 매칭시켜서 map을 만든 후

3. Factory class Create 함수 내부에서 이 map을 활용할 것이다.

 

 

#include <iostream>
#include <vector>
#include <map>
#include <functional>

class IScdOperator
{
public:
    virtual void doOperation() = 0;
    virtual ~IScdOperator() {}
};

class StartOperator : public IScdOperator
{
public:
    void doOperation() override { std::cout << "Start Operator!" << std::endl; }

    static StartOperator* Create() { return new StartOperator; }
};
class StopOperator : public IScdOperator
{
public:
    void doOperation() override { std::cout << "Stop Operator!" << std::endl; }

    static StopOperator* Create() { return new StopOperator; }
};

class ResetOperator : public IScdOperator
{
public:
    void doOperation() override { std::cout << "Reset Operator!" << std::endl; }

    static ResetOperator* Create() { return new ResetOperator; }
};

class ScdOprFactory {
    using CREATOR = std::function<IScdOperator* ()>;
    std::map<std::string, CREATOR> create_map;
    ScdOprFactory(){}
    ~ScdOprFactory(){}
public:
    static ScdOprFactory& getInstance(){
        static ScdOprFactory obj;
        return obj;
    }

    void Regist(std::string cmd, CREATOR create_func) {
        create_map[cmd] = create_func;
    }

    IScdOperator* Create(std::string cmd) {
        if (create_map.find(cmd) == create_map.end())
            return nullptr;

        return create_map[cmd]();
    }
};

int main()
{
    IScdOperator* CustomOpr;
    ScdOprFactory& factory = ScdOprFactory::getInstance();

    factory.Regist("START_OPR", &StartOperator::Create);
    factory.Regist("STOP_OPR", &StopOperator::Create);
    factory.Regist("RESET_OPR", &ResetOperator::Create);

    while (1)
    {
        std::string cmd;
        std::cin >> cmd;

        CustomOpr = factory.Create(cmd);

        if (CustomOpr != nullptr)
            CustomOpr->doOperation();
        else
            std::cout << "Invalid Cmd!" << std::endl;
    }
}

 

 

이런 식으로.

 

이렇게 하면 새로운 Operator 가 추가 될 때 각 class에서 Create함수를 추가로 만들어 주고,

시작하기 전 Regist에 cmd와 create 를 등록해주면 될 것이다.

 

다만.. 이렇게 해도 여전히 OCP는 지켜지지 않는다. (깐깐하게 말하면.)

새로운 operator가 추가될 때마다 main 함수에다 regist 를 추가시켜야 하기 때문에.

 

그러면, static은 코드가 처음 생성될 때 같이 만들어진다는 것을 이용하여 코드를 조금 바꾸어보자.

 

 

 

#include <iostream>
#include <vector>
#include <map>
#include <functional>

class IScdOperator
{
public:
    virtual void doOperation() = 0;
    virtual ~IScdOperator() {}
};

using OPR_CREATOR = std::function<IScdOperator* ()>;
class ScdOprFactory {
private:
    std::map<std::string, OPR_CREATOR > create_map;
    ScdOprFactory() {}
    ~ScdOprFactory() {}
public:
    static ScdOprFactory& getInstance() {
        static ScdOprFactory obj;
        return obj;
    }

    void Regist(std::string cmd, OPR_CREATOR  create_func) {
        create_map[cmd] = create_func;
    }

    IScdOperator* Create(std::string cmd) {
        if (create_map.find(cmd) == create_map.end())
            return nullptr;

        return create_map[cmd]();
    }
};

struct AutoRegist {
    AutoRegist(std::string cmd, OPR_CREATOR create_func) {
        ScdOprFactory::getInstance().Regist(cmd, create_func);
    }
};

class StartOperator : public IScdOperator
{
public:
    void doOperation() override { std::cout << "Start Operator!" << std::endl; }

    static StartOperator* Create() { return new StartOperator; }
    static AutoRegist ar;
};
AutoRegist StartOperator::ar("START_OPR", &StartOperator::Create);


class StopOperator : public IScdOperator
{
public:
    void doOperation() override { std::cout << "Stop Operator!" << std::endl; }

    static StopOperator* Create() { return new StopOperator; }
    static AutoRegist ar;
};
AutoRegist StopOperator::ar("STOP_OPR", &StopOperator::Create);


class ResetOperator : public IScdOperator
{
public:
    void doOperation() override { std::cout << "Reset Operator!" << std::endl; }

    static ResetOperator* Create() { return new ResetOperator; }
    static AutoRegist ar;
};
AutoRegist ResetOperator::ar("RESET_OPR", &ResetOperator::Create);




int main()
{
    IScdOperator* CustomOpr;
    ScdOprFactory& factory = ScdOprFactory::getInstance();

    while (1)
    {
        std::string cmd;
        std::cin >> cmd;

        CustomOpr = factory.Create(cmd);

        if (CustomOpr != nullptr)
            CustomOpr->doOperation();
        else
            std::cout << "Invalid Cmd!" << std::endl;
    }
}

 

 

 

이런 식으로.

 

각 operator class에 ar이라는 static 변수가 추가되었다. 이것은 코드가 맨 처음 시작할 때  AutoRegist의 생성자를 부르고, 이 생성자 안에서 각 opr의 cmd와 create 함수를 매칭시켜 준다.

 

이렇게 하면 완벽하게 OCP 가 지켜지게 된다. 만약 새로운 Operator가 추가된다고 하더라도, New_Operator.cpp 를 만든 후 ,  그 안에 Create 함수와 AutoRegist static 변수를 생성해 주면 될 것이다.

 

 

이것을 매크로를 이용하여 조금만 더 이쁘게 만들면

 

 

 

#include <iostream>
#include <vector>
#include <map>
#include <functional>

#define SET_CREATOR(Custom_OprClass)                                \
static Custom_OprClass* Create() { return new Custom_OprClass; }    \
static AutoRegist ar;

#define REGIST_CREATOR(CustomCmd, Custom_OprClass) AutoRegist StartOperator::ar("CustomCmd", &Custom_OprClass::Create);

class IScdOperator
{
public:
    virtual void doOperation() = 0;
    virtual ~IScdOperator() {}
};

using OPR_CREATOR = std::function<IScdOperator* ()>;
class ScdOprFactory {
private:
    std::map<std::string, OPR_CREATOR > create_map;
    ScdOprFactory() {}
    ~ScdOprFactory() {}
public:
    static ScdOprFactory& getInstance() {
        static ScdOprFactory obj;
        return obj;
    }

    void Regist(std::string cmd, OPR_CREATOR  create_func) {
        create_map[cmd] = create_func;
    }

    IScdOperator* Create(std::string cmd) {
        if (create_map.find(cmd) == create_map.end())
            return nullptr;

        return create_map[cmd]();
    }
};

struct AutoRegist {
    AutoRegist(std::string cmd, OPR_CREATOR create_func) {
        ScdOprFactory::getInstance().Regist(cmd, create_func);
    }
};

class StartOperator : public IScdOperator
{
public:
    void doOperation() override { std::cout << "Start Operator!" << std::endl; }

    SET_CREATOR(StartOperator)
};
REGIST_CREATOR("START_OPR", StartOperator);


class StopOperator : public IScdOperator
{
public:
    void doOperation() override { std::cout << "Stop Operator!" << std::endl; }

    SET_CREATOR(StopOperator)
};
REGIST_CREATOR("STOP_OPR", StopOperator);


class ResetOperator : public IScdOperator
{
public:
    void doOperation() override { std::cout << "Reset Operator!" << std::endl; }
    SET_CREATOR(ResetOperator)
};
REGIST_CREATOR("RESET_OPR", ResetOperator);


int main()
{
    IScdOperator* CustomOpr;
    ScdOprFactory& factory = ScdOprFactory::getInstance();

    while (1)
    {
        std::string cmd;
        std::cin >> cmd;

        CustomOpr = factory.Create(cmd);

        if (CustomOpr != nullptr)
            CustomOpr->doOperation();
        else
            std::cout << "Invalid Cmd!" << std::endl;
    }
}

 

 

 

전체 코드는 위와 같이 될 것이다.

 

 

 

 

 

 

* 잘못된 정보는 알려주시면 감사하겠습니다.

반응형

'Design Pattern' 카테고리의 다른 글

iterator pattern  (0) 2022.11.20
Template Method  (0) 2022.09.06
Observer Pattern  (0) 2022.08.14
Decorator pattern  (0) 2022.07.26
Composite Pattern  (0) 2022.05.18