예시 코드로 생성 패턴을 정리해보고 각 구현의 차이점을 알아보자.
추상 팩토리 Abstract Factory
- 만들고자하는 인스턴스에 대한 정보가 담기거나 타입이 결정된 공장 객체를 통해 인스턴스를 생성하는 방법.
- 주로 개별 객체를 만드는 것이 아니라 여러 객체가 합쳐진 시스템이나 환경을 만들기 위해 사용한다.
- 그러한 전체 시스템 객체의 생성자나 Create()와 같은 연산으로 참조자를 반환하는 방법으로 객체를 생성하고
- 매개변수로 추상 팩토리를 전달하면 원하는 시스템이 만들어진다.
팩토리 메서드 Factory Method
- 생성자가 아닌 함수 호출로 인스턴스를 생성하는 방법으로 클래스 타입을 동적으로 설정할 수 있다는 장점이 있다.
- 객체 생성시 가장 흔히 쓰이는 방법.
- 매개변수로 생성하는 클래스 타입을 알아보기 위한 인자를 전달하거나 템플릿 방법을 쓸 수 있다.
추상 팩토리와 팩토리 메서드는 혼합해서 사용할 수 있는데 예시 코드에서 알아보도록 한다.
기본 클래스
- Window 창에 그릴 수 있는 그래픽 요소를 정의하는 추상 클래스 Graphic과 그 밖에 파생클래스들을 나타낸다.
- 기본적으로 원, 사각형, 이미지, 텍스트 객체만을 다루는 것으로 한다.
팩토리 메서드를 사용하는 추상 팩토리 GraphicFactory 클래스를 다음과 같이 구현할 수 있다.
class GraphicFactory
{
public:
GraphicFactory();
~GraphicFactory();
// 팩토리 메서드를 이용
virtual Rectangle* MakeRectangle(int w, int h);
virtual Circle* MakeCircle(int radius);
virtual Image* MakeImage(const char* file_name);
virtual Text* MakeText(const char* text, int white_space);
};
// 예시
Image* GraphicFactory::MakeImage(const char* file_name)
{
return new Image(file_name);
}
Text* GraphicFactory::MakeText(const char* text, int white_space)
{
return new Text(text, white_space);
}
- MakeXXX() 메서드는 팩토리 메서드로 연산을 통해 은닉된 방식으로 해당 인스턴스를 생성하여 참조자를 반환한다.
실제 사용자 코드에서는 다음과 같이 나타낼 수 있다.
Window* wnd = new Window(0, 0, 1280, 960);
// 팩토리 메서드 방식
GraphicFactory factory;
int draw_at_x = 200, draw_at_y = 400;
Graphic* rect = factory.MakeRectangle(200, 200);
rect->SetCenter(draw_at_x, draw_at_y);
rect->Draw(wnd);
Graphic* image = factory.MakeImage("test.jpg");
image->SetCenter(draw_at_x, draw_at_y);
image->Draw(wnd);
Graphic* text = factory.MakeText("안녕하세요.", 5);
text->SetCenter(draw_at_x, draw_at_y);
text->Draw(wnd);
- 하지만 보통 개별 객체를 생성해서 사용하는 것을 외부에 나타나지 않으며 은닉된 하나의 시스템을 만드는 과정에 포함되어 사용된다.
StandardWidget 클래스와 이를 찍어내는 WidgetGraphicFactory 클래스 추상 팩토리 패턴
- StandardWidget이라는 클래스는 내부에 이미지와 텍스트로 이루어진 객체로 객체들을 모은 시스템이라고 할 수 있다.
- 내부에서 객체들을 소유하고 Draw() 연산시 알맞게 Window 창에 띄우도록한다.
- StandardWidget의 생성 방식은 생성자나 다른 CreateWidget()등으로 구현할 수 있는데, 간단하게 생성자에 factory 매개변수를 전달함으로 구현하였다.
- Image와 Text의 구체 클래스 AlignedImage과 AlignedText를 사용하고 있다.
- 추상 팩토리는 Widget을 생산할 수 있는 인터페이스 구현을 재정의하여 (GraphicFactory를 상속) 새로운 WidgetGraphicFactory 클래스를 정의할 수 있다.
WidgetGraphicFactory.h
위젯 생성을 위한 추상 팩토리의 구체 클래스로서 한 예시이다.
class WidgetGraphicFactory : public GraphicFactory
{
int _mode;
public:
WidgetGraphicFactory(int m);
~WidgetGraphicFactory();
virtual Image* MakeImage(const char* file_name) override;
virtual Text* MakeText(const char* text, int white_space) override;
};
Image* WidgetGraphicFactory::MakeImage(const char* file_name)
{
return new ImageAligned(file_name, _mode);
}
Text* WidgetGraphicFactory::MakeText(const char* text, int white_space)
{
return new TextAligned(text, white_space, _mode);
}
- WidgetGraphicFactory는 ImageAligned 객체와 TextAligned 객체를 생성하는 구체 추상 팩토리이다.
StandardWidget.h
#include "Image.h"
#include "Text.h"
#include "GraphicFactory.h"
#include <vector>
#include <string>
enum WIDGET_TYPE
{
WIDE = 0, REASONABLE, NARROW
};
class StandardWidget
{
private:
int sp;
int width, height;
int rows, cols;
Graphic**_images;
Graphic** _texts;
public:
StandardWidget(WIDGET_TYPE widget,
std::vector<std::string> &files, std::vector<std::string> &texts ,
GraphicFactory& factory);
~StandardWidget();
int GetRows() const;
int GetCols() const;
void Insert(int at_row, int at_col, Graphic* src);
void Delete(int at_row, int at_col);
// rows, cols 에 맞는 이미지, 텍스트를 자동으로 Resize하여 화면에 띄움
void Draw(Window*);
};
- StandardWidget은 행과 열로 정렬된 Text와 Image를 띄우기 위해 설계된 클래스이다.
- 생성자에서 GraphicFactory 추상 팩토리를 매개변수로 전달받고 있다.
- 실제 StandardWidget이 만들어지는 과정
#include "StandardWidget.h"
StandardWidget::StandardWidget(WIDGET_TYPE widget,
std::vector<std::string>& files, std::vector<std::string>& texts,
GraphicFactory& factory)
{
// 위젯 설정
switch (widget)
{
// 각 종류별 위젯 생성
case WIDE:
{
}break;
case REASONABLE:
{
}break;
case NARROW:
{
}break;
}
_images = new Graphic * [files.size()];
_texts = new Graphic * [texts.size()];
// 추상 팩토리를 매개변수로 받아 자동 생성
for (int f_id = 0; f_id < files.size(); f_id++)
{
// _images[f_id] = new Image(files[f_id].c_str());
_images[f_id] = factory.MakeImage(files[f_id].c_str());
}
for (int f_id = 0; f_id < files.size(); f_id++)
{
// _texts[f_id] = new Text(texts[f_id].c_str(), sp)
_texts[f_id] = factory.MakeText(texts[f_id].c_str(), sp);
}
}
실제 사용자 코드에서는 StandardWidget인스턴스가 만들어지는 과정이 은닉화된다.
int main()
{
// 추상 팩토리 방식으로 위젯을 생성
vector<string> files, texts;
WidgetGraphicFactory widgetfactory(ALIGN::CENTER);
// 추상 팩토리 인스턴스를 전달한다.
StandardWidget* widget = new StandardWidget(WIDGET_TYPE::WIDE, files, texts, widgetfactory);
widget->Draw(wnd);
return 0;
}
- 매개변수로 widgetfactory가 전달된 것을 볼 수 있다.
프로토타입 (Prototype)
- 객체를 직접 생성하지 않고 미리 생성된 인스턴스의 복제품을 사용하는 방식이다.
- 사용자가 객체를 사용하기에 상태 변경이 크지 않은 방식으로 재사용 가능할 때 적용할 수 있다.
- 프로토타입 관리자를 단일체 패턴으로 정의하고 프로토타입 원본 인스턴스들을 관리하게 한다.
- Clone() 연산을 통해 원본과 똑같은 내용을 가진 새로운 인스턴스를 생성하여 반환하게 한다.
단일체 패턴 (Singleton)
- 프로그램 전체에서 유일한 인스턴스임을 보장하고 객체 접근에 대한 인터페이스를 제공한다.
- 사용자 코드에서 생성자를 호출하지 못 하도록 막고 접근 시 인스턴스화가 일어난다.
프로토타입 관리자를 단일체로 구현해보고 사용자 코드에서 예시를 알아본다.
ProtoTypeMangerSingle.h
class ProtoTypeManager
{
public:
static ProtoTypeManager* Instance();
Graphic* GetPrototype(const char*);
void Init();
void Release();
protected:
ProtoTypeManager();
private:
Circle* _p_circle = nullptr;
Rectangle* _p_rect = nullptr;
Text* _p_text = nullptr;
static ProtoTypeManager* _instance;
};
- 프로토타입 관리자는 단일체로 구현되어 있고 프로토타입의 원본을 관리하고 있다.
#include "ProtoTypeManager.h"
ProtoTypeManager* ProtoTypeManager::_instance = nullptr;
ProtoTypeManager::ProtoTypeManager()
{
}
ProtoTypeManager* ProtoTypeManager::Instance()
{
if (_instance == 0)
{
_instance = new ProtoTypeManager;
// 프로토타입 초기화
_instance->Init();
}
return _instance;
}
void ProtoTypeManager::Release()
{
if (_instance)
{
delete _instance;
_instance = nullptr;
}
}
Graphic* ProtoTypeManager::GetPrototype(const char* query)
{
if (strcmp(query, "Circle"))
{
// 복사 생성자를 이용
// return Circle(*this);
return _p_circle->Clone();
}
// ... else
}
void ProtoTypeManager::Init()
{
_p_circle = new Circle(5);
_p_rect = new Rectangle(100, 100);
_p_text = new Text("text", 10);
}
- 원형관리자는 Init()을 통해 원형 인스턴스를 초기화하도록 한다.
- GetPrototype() 함수를 통해 복사 생성하고자 하는 클래스를 문자열로 받아 복사 생성하도록 한다.
- 유연한 원형 패턴
class ProtoTypeManager
{
private:
Circle* _p_circle;
Rectangle* _p_rect;
Text* _p_text;
public:
ProtoTypeManager(Circle*, Rectangle*, Text*);
Circle* MakeCircle() { return _p_circle->Clone(); }
Rectangle* MakeRectangle() { return _p_rect->Clone(); }
};
- 위처럼 사용하는 것이 바람직하고 생성자 인자를 통해 동적으로 구체적인 클래스 타입을 결정하도록한다.
- 프로토타입 패턴은 상태를 변경하여 사용할 수 있으므로 Set 메서드를 지원하는 것이 좋다.
프로토타입 관리자를 이용하여 다음과 같이 복제 생성한 인스턴스를 다음과 같이 사용할 수 있다.
'디자인 패턴 > GoF' 카테고리의 다른 글
[디자인 패턴] 행동 패턴 (2) 명령 Command (0) | 2021.02.02 |
---|---|
[디자인 패턴] 행동 패턴 (1) 책임 연쇄 Chain of Responsiblity (0) | 2021.02.01 |
[디자인 패턴] 구조 패턴 정리 (0) | 2021.01.30 |
[디자인 패턴] 구조 패턴 (7) 프록시 Proxy (0) | 2021.01.30 |
[디자인 패턴] 구조 패턴 (6) 플라이급 Flyweight (0) | 2021.01.29 |