- 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로 빼 보자.
#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 부분만 변경하면 된다.
사실 여기서 팩토리 메서드는 끝나지만...
팩토리 메서드 패턴 - 위키백과, 우리 모두의 백과사전
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 |