디자인 패턴 (싱글턴, 프로토타입, 팩토리)
싱글턴 (Singleton)
오직 하나의 인스턴스만 존재하게 하고, 어디서든 접근 가능하게 하는 패턴
- 목적
- 인스턴스를 하나만 만들어서 공유할 때 사용 (ex. 설정, 로그, DB 연결 등)
-
특징
- 전역 접근 지점 제공
- 생성자를 private으로 막고, getInstance() 같은 메서드를 통해 접근
1
2
3
4
5
6
7
8
9
10
11
12
class Singleton {
private:
static Singleton* instance;
Singleton() {} // private 생성자
public:
static Singleton* getInstance() {
if (!instance)
instance = new Singleton();
return instance;
}
};
프로토타입 (Prototype)
기존 객체를 복제하여 새로운 객체를 생성하는 패턴
- 목적
- 객체 생성 속도를 비약적으로 올리기 위해 사용.
-
특징
-
clone()
함수를 통해 객체를 복제 - 복제된 객체는 원본과 독립적
-
프로토 타입을 쓰면 객체 생성 속도가 빨라지는 이유는?
프로토타입을 하더라도 클론하게 되면 new를 하는 것이다. new속도와 비교하는 것은 의미 없다.
플레이팹, AWS, Gamebase, 뒤끝 등 여러 서버 엔진이 있다.
모든 스테이터스에 대한 정보는 서버가 관리한다. 몬스터끼리의 충돌을 구현한다는 것도 서버 안에서 로직이 돌아가는 경우가 많다.
우리는 보스라는 몬스터를 생성 할 때 스테이터스를 서버로부터 받아온다. 이는 마치 파일 입출력처럼 느리다. 몬스터를 하나 생성할 때 마다 서버로부터 정보를 가져오게 된다면…엄청 느려진다. 그래서 서버로부터 정보를 받아오는 작업을 로딩에서 해주자. 원형만 따라락 받아놓으면 서버와의 통신은 더 이상 필요하지 않다. 서버로부터 받아온 데이터를 단순한 복사를 통해 몬스터들을 만들어준다. 훨씬 더 빠르게 생성할 수 있게 된다.
특정 객체를 생성할 때 이 객체의 정보가 서버, 또는 파일 입출력을 통해서 이루어져야 한다면 인게임 내에서 런타임에서 하기에는 느려진다. 필요한 패킷 통신이 필요하면 원형을 미리 만들어 두고, 복제해서 만드는 게 훨씬 빨라질 것이다.
이 객체를 만들 때 생각보다 오래 시간이 필요하다. 이유는 서버간의 통신같은게 필요해서 오래 걸리는 것이라고 생각해 볼 수 있다?실제 게임을 만들 땐 시연회때 처럼 보스의 이니셜라이즈에 스테이터스 넣어줘야지~하지 않는다.
서버로부터 정보를 받아와야 하는 상황에서 프로토타입을 사용 했을 때 속도가 빨라지는 효과가 있다.
1
2
3
4
5
6
7
8
9
10
11
class Prototype {
public:
virtual Prototype* clone() const = 0;
};
class Concrete : public Prototype {
public:
Concrete* clone() const override {
return new Concrete(*this); // 복제 생성자
}
};
팩토리 (Factory Method)
객체 생성을 자식 클래스에게 맡기고, 부모 클래스는 추상 타입만 사용
팩토리 패턴은 객체 생성 로직을 별도의 클래스(팩토리 클래스)에서 관리합니다.
즉, 객체 생성은 공장(팩토리)에서 이루어지며, 클라이언트는 객체를 직접 생성하지 않고 팩토리 클래스에 요청하여 객체를 생성합니다.
-
목적
- 객체 생성 로직을 숨기고, 인터페이스를 통해 객체를 생성하게 함
- 객체 생성의 책임을 서브클래스로 위임하여, 코드의 유연성과 확장성을 높이는 데 목적이 있습니다.
-
특징
-
new
대신 팩토리 메서드를 사용 - 클라이언트는 어떤 클래스가 생성되는지 몰라도 됨
-
예시 상황
게임에서 Monster
라는 클래스가 있고, 게임의 각 지역마다 서로 다른 종류의 몬스터를 생성해야 할 때
createMonster()
라는 팩토리 메서드를 정의해두고,
각 지역별로 이를 상속한 클래스가 고유 몬스터를 생성하도록 구현
→ 이렇게 하면, 몬스터 생성 로직이 지역마다 유연하게 분리되고,
새로운 지역을 추가해도 기존 코드를 건드릴 필요가 없다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Enemy {
public:
virtual void attack() = 0;
};
class Orc : public Enemy {
public:
void attack() override { std::cout << "Orc attacks!\n"; }
};
class EnemyFactory {
public:
virtual Enemy* create() = 0;
};
class OrcFactory : public EnemyFactory {
public:
Enemy* create() override { return new Orc(); }
};
패턴 | 목적 | 특징 |
---|---|---|
Singleton | 하나의 인스턴스만 유지 | 전역 접근, 인스턴스 재사용 |
Prototype | 복제를 통한 객체 생성 | clone 메서드, 성능/구조 최적화 |
Factory | 객체 생성 책임 분리 | 인터페이스 기반 생성, 유연한 확장성 |
항목 | 팩토리 메서드 패턴 | 추상 팩토리 패턴 |
---|---|---|
의도 | 객체 생성을 서브클래스에게 위임 | 관련된 객체들의 “제품군”을 생성 |
생성 단위 | 단일 객체 | 여러 관련 객체(제품군) |
확장 방식 | 서브클래스를 통해 생성 로직 확장 | 제품군을 추가한 새 팩토리 클래스 작성 |
클라이언트는? | 객체 생성을 팩토리 메서드에 위임 | 어떤 구체 클래스가 생성되는지 모름 |
항목 | 팩토리 패턴 | 프로토타입 패턴 |
---|---|---|
객체 생성 방식 | 서브클래스에서 new 로 객체 생성 |
기존 객체를 복제(clone) 해서 생성 |
핵심 개념 | 객체 생성을 하위 클래스에 위임 | 객체 복사로 생성 비용 절감 |
장점 | 객체 생성 과정을 캡슐화, 타입 확장에 유리 | 복잡한 객체의 생성 비용 감소, 동적 생성에 유리 |
사용 시기 | 어떤 객체를 만들지 결정해야 할 때 | 동일한 객체를 여러 개 생성할 때, 생성 비용이 클 때 |
대표 메서드 |
create() 같은 팩토리 메서드 |
clone() 메서드 |