싱글턴 패턴은 오직 한 개의 클래스 인스턴스만을 갖도록 보장하고, 이에 대한 전역적인 접근점을 제공한다.

 

싱글턴 패턴의 특징

1. 오직 한 개의 클래스 인스턴스만 갖도록 보장

시스템에서 하나만 있어야 하는 객체는 GameManager나 파일 시스템 클래스와 같은 것들이 있다.

 

2. 전역 접근점을 제공 (전역 변수로써 기능)

class FileSystem {
  public:
    static FileSystem& instance() {
      // 게으른 초기화
      if (instance_ == NULL) {
        instance_ = new FileSystem();
      }
    return *instance_;
  private:
    FileSystem() {}
    static FileSystem* instance_;
};  


class FileSystem{
  public:
    static FileSystem& instance() {
      static FileSystem *instance = new FileSystem();
      return *instance;
    }
    
  private:
    FileSystem() {}
};

어디서든 필요할 때 마다 instance 함수로 클래스 인스턴스를 얻을 수 있다. (인스턴스 정보를 수정할 수 있다.)

FileSystem instance = FileSystem.instance()

 

3. 한 번도 사용하지 않는다면 아예 인스턴스를 생성하지 않는다.

ㄴ 싱글턴은 처음 사용될 때 초기화되므로 게임 내에서 사용되지 않는다면 초기화 되지 않는다. (메모리 사용량 감소)

 

4. 런타임에 초기화된다.

ㄴ 컴파일러에서 정적 변수 초기화 순서를 보장해주지 않기 때문에 정적 변수 사이 안전한 의존 관계를 만들 수 없는데 

게으른 초기화는 이런 문제를 해결해 준다.

 

5. 싱글톤을 상속하여 사용할 수도 있다.

ㄴ 싱글턴 객체가 플랫폼 별로 다르게 정의되어야 하는 경우 사용할 수 있음

 

FileSystem& FileSystem::instance() { // filesystem을 싱글턴으로 만든다.
#if PLATFORM == PLAYSTATION3
  static FileSystem *instance = new PS3FileSystem(); // FileSystem을 상속받는 ps3 전용 filesystem
#elif PLATFORM == WII
  static FileSystem *instance = new WiiFileSystem(); 
#endif
  return *instance;
}

단점

싱글턴 인스턴스는 어디에서든 접근 할 수 있기 때문에 전역 변수로써 기능한다.

= 전역변수의 단점

1. 멀티 스레딩 같은 동시성 프로그래밍에 알맞지 않다.

2. 전역 변수는 커플링을 조장한다.

3. 전역 변수는 코드를 이해하기 어렵다. (어디서든 상태를 변경할 수 있기 때문에 변경하는 부분을 찾기 어렵다.)

 

4. 싱글턴은 문제가 하나뿐일 때도 두 가지 문제를 풀려고 한다.

ㄴ 오직 한 개의 인스턴스를 가지는 기능과 전역 접근 기능 중 어느 한 가지 기능만 따로 사용할 수는 없다.

 

5. 게으른 초기화는 제어할 수 없다.

게임 런타임중, 오디오 시스템 초기화나 파일 시스템 초기화가 일어나면 프레임이 떨어질 수 있다.

게임에서는 메모리 단편화를 막기 위해 힙에 메모리를 할당하는 방식을 세밀하게 제어하는데, 이 때문에 힙 어디에 메모리를 할당할지를 제어할 수 있도록 적절한 초기화 시점을 찾아야 한다.

싱글턴 대신 정적 클래스를 사용하고 정적 함수를 사용하는 것이 더 간단할 수 있다. 이러면 instance() 함수를 호출할 필요도 없다. 클래스이름::함수() 로 바로 함수를 호출할 수 있다.

싱글턴의 대안

1. 한 개의 인스턴스만 보장하는 기능

ㄴ 인스턴스가 이미 생성되었는지 여부를 단언문으로 확인

ㄴ ex) assert(!instantiated_);

 

2. 인스턴스에 쉽게 접근하는 기능

ㄴ 방법 1. 객체를 필요로 하는 함수에 인수로 넘겨주기

 

ㄴ 방법 2. 상위 클래스로부터 얻기

상위 클래스에 객체를 반환하는 함수를 만든다. 함수를 protected로 선언하면 그 클래스를 상속받은 코드에서만 객체에 접근할 수 있게 된다.

 

ㄴ 방법 3. 이미 전역인 객체로부터 얻기

전역에서 접근할 수 있는 Game 클래스가 있다고 가정하면 Log, FileSystem, Audio, Player를 각각 싱글턴으로 만드는 대신

Game 객체 하나만 싱글턴으로 만들고, Log, FileSystem, Audio, Player는 Game 클래스의 멤버 객체로 만드는 방법을 사용할 수 있다.

 

+ Recent posts