Design pattern in C++ - 객체를 생성하는 방법[6]
이번 시간에는 객체 생성 관련 패턴을 알아보겠습니다.
객체 생성 방법
1. 사용자가 직접 객체를 생성(stack or heap)
- 가장 자유로운 방법이나 객체 생성에 대한 제약이 없음
class Shape
{
public:
virtual ~Shape() {}
};
class Rect : public Shape
{
public:
};
class Circle : public Shape
{
public:
};
int main()
{
Rect r;
Shape* p = new Rect;
}
2. static 멤버 함수를 사용한 객체 생성
- 생성자를 private 처리하고 객체 생성 함수를 제공
- 객체 생성을 한 곳에서 수행
- 다양한 제약 조건을 만들 수 있음(개수 제한, 자원 공유 등)
- 객체 생성 함수를 함수의 생성 인자로 전달 할 수 있음
class Shape
{
public:
virtual ~Shape() {}
};
class Rect : public Shape
{
Rect() {} // private 생성자.
public:
static Shape* Create() { return new Rect;}
};
class Circle : public Shape
{
Circle() {}
public:
static Shape* Create() { return new Circle;}
};
// 도형을 만들어서 그림을 그리는 함수, 함수 포인터 전달
void CreateAndDraw(Shape* (*f)() )
{
Shape* p = f();
p->Draw();
}
int main()
{
CreateAndDraw(&Rect::Create );
}
3. 객체 생성을 위한 전용 클래스 사용
- 객체 생성을 한곳에 집중시킴
- 객체 본래의 기능과, 생성을 위한 코드를 분리
class Shape
{
public:
virtual ~Shape() {}
};
class Rect : public Shape
{
Rect() {}
public:
friend class ShapeFactory;
};
class Circle : public Shape
{
Circle() {}
public:
friend class ShapeFactory;
};
class ShapeFactory
{
public:
Shape* CreateShape( int type )
{
Shape* p = 0;
switch( type )
{
case 1: p = new Rect; break;
case 2: p = new Circle; break;
}
return p;
}
};
int main()
{
ShapeFactory factory;
Shape* p = factory.CreateShape(1);
}
4. 기존 존재하는 객체를 복사해서 새로운 객체 생성
- prototype
class Shape
{
public:
virtual ~Shape() {}
virtual Shape* Clone() = 0;
};
class Rect : public Shape
{
public:
virtual Shape* Clone() { return new Rect(*this);}
};
class Circle : public Shape
{
public:
virtual Shape* Clone() { return new Circle(*this);}
};
int main()
{
Shape* p = new Rect;
Shape* p2 = p->Clone();
}
Singleton pattern
정의
- 클래스의 인스턴스가 오직 하나임을 보장하며 이에 대한 접근은 어디에서 든지 하나로 통일하여 제공
구현
- 생성자를 private 처리
- static 멤버 함수를 통해서 오직 하나의 객체를 생성한 후 참조를 반환
#include <iostream>
using namespace std;
// CRTP : Curiously Recurring Template Pattern
template<typename TYPE> class Singleton
{
protected:
Singleton() { }
Singleton(const Singleton&) = delete;
void operator=(const Singleton&) = delete;
public:
static TYPE& getInstance()
{
static TYPE instance;
return instance;
}
};
class Mouse : public Singleton< Mouse >
{
};
int main()
{
Mouse& c1 = Mouse::getInstance();
}
Factory pattern
정의
- 새로운 객체가 필요 할 때 다형적 생성이 가능한 구조를 제공하는 패턴
#include <iostream>
#include <vector>
#include <map>
#include "Singleton.hpp"
using namespace std;
class Shape
{
public:
virtual void Draw() { cout << "Draw Shape" << endl;}
};
class ShapeFactory
{
MAKE_SINGLETON(ShapeFactory)
typedef Shape* (*CREATOR)();
map<int, CREATOR> create_map;
public:
void Register( int type, CREATOR f )
{
create_map[type] = f;
}
Shape* CreateShape(int type )
{
Shape* p = 0;
auto ret = create_map.find( type );
if ( ret == create_map.end() )
return 0;
p = create_map[type]();
return p;
}
};
struct RegisterShape
{
RegisterShape( int type, Shape*(*f)() )
{
ShapeFactory& factory = ShapeFactory::getInstance();
factory.Register(type, f);
}
};
// 모든 도형이 지켜야 하는 규칙을 매크로로 제공
#define DECLARE_SHAPE( classname ) \
static Shape* Create() { return new classname;} \
static RegisterShape rs;
#define IMPLEMENT_SHAPE( type, classname ) \
RegisterShape classname::rs(type, &classname::Create);
class Circle : public Shape
{
public:
virtual void Draw() { cout << "Circle Rect" << endl;}
DECLARE_SHAPE( Circle )
};
IMPLEMENT_SHAPE( 1, Circle )
class Triangle : public Shape
{
public:
virtual void Draw() { cout << "Triangle Rect" << endl;}
DECLARE_SHAPE( Triangle )
};
IMPLEMENT_SHAPE( 2, Triangle )
int main()
{
ShapeFactory& factory = ShapeFactory::getInstance();
vector<Shape*> v;
while( 1 )
{
int cmd;
cin >> cmd;
//
if ( cmd >=1 && cmd <= 5 )
{
Shape* p = factory.CreateShape(cmd);
if ( p != 0 )
v.push_back( p );
}
else if ( cmd == 9 )
{
for ( auto p : v )
p->Draw(); // 다형성
}
}
}
factory 패턴 응용 -> Prototype 패턴
- 같은 타입에, 데이터가 다를 경우 -> 이것을 복사해서 전달하는 형태
#include <iostream>
#include <vector>
#include <map>
#include "Singleton.hpp"
using namespace std;
class Shape
{
public:
virtual void Draw() { cout << "Draw Shape" << endl;}
virtual Shape* Clone() = 0;
};
class Rect : public Shape
{
public:
virtual void Draw() { cout << "Draw Rect" << endl;}
static Shape* Create() { return new Rect;}
virtual Shape* Clone() { return new Rect(*this);}
};
class Circle : public Shape
{
public:
virtual void Draw() { cout << "Circle Rect" << endl;}
static Shape* Create() { return new Circle;}
virtual Shape* Clone() { return new Circle(*this);}
};
class ShapeFactory
{
MAKE_SINGLETON(ShapeFactory)
map<int, Shape*> protype_map;
public:
void Register( int type, Shape* sample )
{
protype_map[type] = sample;
}
Shape* CreateShape(int type )
{
Shape* p = 0;
auto ret = protype_map.find( type );
if ( ret == protype_map.end() )
return 0;
p = protype_map[type]->Clone();
return p;
}
};
int main()
{
ShapeFactory& factory = ShapeFactory::getInstance();
Rect* r1 = new Rect;// 빨간색 크기 5
Rect* r2 = new Rect;// 파란색 크기 10
// 공장에 객체 등록
factory.Register( 1, r1);
factory.Register( 2, r2);
vector<Shape*> v;
while( 1 )
{
int cmd;
cin >> cmd;
if ( cmd >=1 && cmd <= 5 )
{
Shape* p = factory.CreateShape(cmd);
if ( p != 0 )
v.push_back( p );
}
else if ( cmd == 9 )
{
for ( auto p : v )
p->Draw(); // 다형성
}
}
}
Abstract Factory pattern
정의
- 여러 객체의 군을 생성하기 위한 인터페이스를 제공
#include <iostream>
#include <string.h>
using namespace std;
struct IEdit
{
virtual void Draw() = 0;
virtual ~IEdit() {}
};
struct IButton
{
virtual void Draw() = 0;
virtual ~IButton() {}
};
struct WinButton : public IButton { void Draw() { cout << "Draw WinButton" << endl;}};
struct GTKButton : public IButton{ void Draw() { cout << "Draw GTKButton" << endl;}};
struct WinEdit : public IEdit { void Draw() { cout << "Draw WinEdit" << endl;}};
struct GTKEdit : public IEdit { void Draw() { cout << "Draw GTKEdit" << endl;}};
//-------------------------------------
// Factory 의 공통의 기반 클래스
struct IFactory
{
virtual IButton* CreateButton() = 0;
virtual IEdit* CreateEdit() = 0;
virtual ~IFactory() {}
};
struct WinFactory : public IFactory
{
IButton* CreateButton() { return new WinButton;}
IEdit* CreateEdit() { return new WinEdit;}
};
struct GTKFactory : public IFactory
{
IButton* CreateButton() { return new GTKButton;}
IEdit* CreateEdit() { return new GTKEdit;}
};
int main(int argv, char** argc)
{
IFactory* pFactory;
if ( strcmp(argc[1], "Windows") == 0)
pFactory = new WinFactory;
else
pFactory = new GTKFactory;
IButton* pBtn = pFactory->CreateButton();
pBtn->Draw();
}
Factory Method pattern
정의
- 객체를 생성하기 위한 인터페이스를 정의 하지만, 어떤 인스턴스로 생석 할 지에 대한 결정은 하위 클래스에서 재정의
- Factory Method 패턴에서는 클래스의 인스턴스 생성 시점을 하위 클래스로 연기함
- template method와 유사한 형태
- 리마인드 : 템플릿 메소드는 알고리즘이나 정책을 하위 클래스에서 재정의 하는 것
#include <iostream>
using namespace std;
struct IEdit
{
virtual void Draw() = 0;
virtual ~IEdit() {}
};
struct IButton
{
virtual void Draw() = 0;
virtual ~IButton() {}
};
struct WinButton : public IButton { void Draw() { cout << "Draw WinButton" << endl;}};
struct GTKButton : public IButton { void Draw() { cout << "Draw GTKButton" << endl;}};
struct WinEdit : public IEdit { void Draw() { cout << "Draw WinEdit" << endl;}};
struct GTKEdit : public IEdit { void Draw() { cout << "Draw GTKEdit" << endl;}};
//---------------------------------------------------
class BaseDialog
{
public:
void Init()
{
IButton* pBtn = CreateButton();
IEdit* pEdit = CreateEdit();
pBtn->Draw();
pEdit->Draw();
}
virtual IButton* CreateButton() = 0;
virtual IEdit* CreateEdit() = 0;
};
class WinDialog : public BaseDialog
{
public:
virtual IButton* CreateButton() { return new WinButton;}
virtual IEdit* CreateEdit() { return new WinEdit;}
};
class GTKDialog : public BaseDialog
{
public:
virtual IButton* CreateButton() { return new GTKButton;}
virtual IEdit* CreateEdit() { return new GTKEdit;}
};
int main()
{
WinDialog dlg;
dlg.Init();
}
Builder pattern
정의
- 객체를 생성하는 방법과 표현하는 방법을 정의하는 클래스를 별도로 분리하여 서로 다른 표현이라도 이를 생성 할 수 있는 동일한 인터페이스를 제공하도록 함
- state / strategy 패턴과 유사
- 전략 패턴은 교체 되는 부분이 -> 알고리즘
- 상태 패턴은 교체 되는 부분이 -> 행동
#include <iostream>
#include <string>
using namespace std;
// 입학 지원서
using Application = string; // class Application {}
// 지원서의 각 단계의 표현을 만드는 빌더 인터페이스
struct IBuilder
{
virtual ~IBuilder() {}
virtual void makeName(string name) = 0;
virtual void makePhone(string phone) = 0;
virtual void makeAddress(string addr) = 0;
virtual Application getResult() = 0;
};
// 지원서 만드는 클래스
class Director
{
string name = "HONG";
string phone = "010-111-1111";
string address = "SEOUL KANGNAMGU";
IBuilder* pBuilder;
public:
void setBuilder( IBuilder* p ) { pBuilder = p;}
Application construct()
{
pBuilder->makeName(name);
pBuilder->makePhone(phone);
// pBuilder->makeAddress(address);
return pBuilder->getResult();
}
};
class XMLBuilder : public IBuilder
{
Application app;
public:
virtual void makeName(string name)
{
app += "<NAME>" + name + "</NAME>\n";
}
virtual void makePhone(string phone)
{
app += "<PHONE>" + phone + "</PHONE>\n";
}
virtual void makeAddress(string addr)
{
app += "<ADDRESS>" + addr + "</ADDRESS>\n";
}
virtual Application getResult() { return app;}
};
class TextBuilder : public IBuilder
{
Application app;
public:
virtual void makeName(string name)
{
app += name + "\n";
}
virtual void makePhone(string phone)
{
app += phone + "\n";
}
virtual void makeAddress(string addr)
{
app += addr + "\n";
}
virtual Application getResult() { return app;}
};
int main()
{
Director d;
XMLBuilder xb;
d.setBuilder(&xb);
Application app = d.construct();
cout << app << endl;
TextBuilder tb;
d.setBuilder(&tb);
app = d.construct();
cout << app << endl;
}
전체 요약
GoF’s 디자인 패턴의 분류
- 생성 패턴 - 5종
- Singleton : 클래스의 인스턴스는 오직 하나임을 보장하며 이에 대한 접근은 어디에서든 하나로만 통일하여 제공
- Abstract Factory : 상세화된 서브 클래스를 정의 하지 않고도 서로 관련성 있거나 독립적인 여러 객체의 군을 생성하기 위한 인터페이스를 제공
- Prototype : 견본 인스턴스를 사용하여 생성할 객체의 종류를 명시하고 견본을 복사하여 새로운 객체를 생성
- Builder : 복잡한 객체를 생성하는 방법과 표현하는 방법을 정의하는 클래스를 별도로 분리하여 서로 다른 표현이라도 이를 생성 할 수 있는 동일한 구축 공정을 제공
- Factory Method : 객체를 생성하기 위한 인터페이스를 정의 하지만, 어떤 클래스의 인터페이스를 생성 할 지에 대한 결정은 서브 클래스에 위임
- 구조 패턴 - 7종
- Adapter, Proxy, Bridge, Facade : 문제를 해결하기 위해 간접층을 만드는 패턴
- Composite, Decorator : 재귀적 포함을 사용하는 패턴
- Flyweight
- 행위 패턴 - 11종
- template method
- strategy
- state
- iterator
- visitor
- observer
- command
- memento
- interpret
- mediator
- chain of responsibility