On this page
article
Object Creation: Singleton, Factory, Prototype, Abstract, Flyweight
Singleton
싱글턴은 프로그램 전체에서 하나의 인스턴스만 존재하도록 보장하는 패턴이다. C++에서는 Meyer’s Singleton이 대표적이며, 정적 지역 변수를 활용해 지연 초기화와 스레드 안전성을 동시에 확보할 수 있다.
class Cursor {
private:
Cursor() {}
Cursor(const Cursor&) = delete;
Cursor& operator=(const Cursor&) = delete;
public:
static Cursor& get_instance() {
static Cursor instance;
return instance;
}
};
// 스레드에서 사용 예시
std::thread t1(&Cursor::get_instance);
std::thread t2(&Cursor::get_instance);
C++11 이전에는 Double-Checked Locking Pattern(DCLP)을 썼지만, 컴파일러 최적화로 인한 문제가 있었고, C++11 이후 해결되었다.
Factory
팩토리 패턴은 객체 생성을 전담하는 클래스를 별도로 분리하는 패턴이다. 생성 로직을 외부로 분리해 코드의 유연성과 확장성을 높여준다.
목적
- 새로운 타입이 추가돼도 클라이언트 코드를 수정하지 않도록 하기 위해
class Shape
{
public:
virtual void draw() = 0;
virtual ~Shape() {}
};
class ShapeFactory
{
MAKE_SINGLETON(ShapeFactory)
using F = Shape*(*)();
std::map<int, F> create_map;
public:
void register_shape(int key, F create_function)
{
create_map[key] = create_function;
}
Shape* create(int type)
{
Shape* p = nullptr;
if (create_map[type] != nullptr)
{
p = create_map[type](); //
}
return p;
}
};
class RegisterFactory
{
public:
RegisterFactory(int type, Shape* (*f)())
{
ShapeFactory::get_instance().register_shape(type, f);
}
};
#define REGISTER
(classname) \
static Shape* create() { return new classname; } \
static RegisterFactory rf;
#define REGISTER_IMPL(type, classname) \
RegisterFactory classname::rf(type, &classname::create);
class Rect : public Shape
{
public:
void draw() override { std::cout << "draw Rect" << std::endl; }
REGISTER(Rect)
};
REGISTER_IMPL(1, Rect)
class Circle : public Shape
{
public:
void draw() override { std::cout << "draw Circle" << std::endl;}
REGISTER(Circle)
};
REGISTER_IMPL(2, Circle)
int main()
{
std::vector<Shape*> v;
ShapeFactory& factory = ShapeFactory::get_instance();
while (1)
{
int cmd;
std::cin >> cmd;
if (cmd > 0 && cmd < 8)
{
Shape* s = factory.create(cmd);
if ( s )
v.push_back(s);
}
else if (cmd == 9)
{
for (auto s : v)
s->draw();
}
}
}
이 구조는 추상 팩토리와도 유사하지만, 간단하게 map을 활용한 방식이다.
Prototype
위 예제는 클래스 자체를 등록했는데 자주 사용하는 객체를 등록하고, 복사하여 생성
Abstract
사용자가 옵션을 무엇을 줬냐에 따라 공장 자체를 바꿔서 사용 RichControlFactory ↔ SimpleControlFactory
Flyweight
Flyweight 패턴은 같은 데이터를 공유해서 메모리 사용을 최소화하는 방식이다. 동일한 속성을 가진 객체는 하나만 만들고, 필요할 때 참조로 재사용한다.
구조 예시: Image Factory
class ImageFactory {
std::map<std::string, Image*> image_map;
public:
Image* create(const std::string& url) {
auto ret = image_map.find(url);
if (ret == image_map.end()) {
Image* img = new Image(url);
image_map[url] = img;
}
return image_map[url];
}
};
구조 예시 2: Word 프로세서에서 문자 공유
class Font { /* ... */ };
class Char {
char value;
Font* font; // 보통 같은 폰트는 공유된다
};