[ Pattern ] Singleton Pattern(싱글턴 패턴) in Unity C#

[ Programming Pattern 시리즈 ]
싱글턴 패턴 (Singleton Pattern) Link
컴포넌트 패턴 (Component Pattern) Link

커멘드 패턴 (Command Pattern) Link
관찰자 패턴 (Observer Pattern) Link
상태 패턴 (State Pattern) Link

Singleton Pattern의 GoF 정의는 아래와 같다.

“오직 한 개의 클래스 인스턴스만을 갖도록 보장하고, 이에 대한 전역적인 접근점을 제공합니다”

싱글턴 패턴은 게임 개발자들이 많이 사용하는 패턴이다. 특히 Manager류의 클래스를 만들거나, 오디오, 이벤트 등 디바이스 I/O를 다루는 곳에서도 자주 쓰인다.

유니티에서 싱글톤 객체를 구현하는 방법은 Scene에 빈객체를 생성한 후에 오직 하나의 객체만 생성되도록 만들고, DontDestroyOnLoad 메쏘드를 호출하여 Scene 변경시에도 Destroy를 막아주는 형태로 구현한다. 아래는 그 예이다.

%e1%84%89%e1%85%b3%e1%84%8f%e1%85%b3%e1%84%85%e1%85%b5%e1%86%ab%e1%84%89%e1%85%a3%e1%86%ba-2016-12-01-%e1%84%8b%e1%85%a9%e1%84%8c%e1%85%a5%e1%86%ab-11-20-59

<GameManager.cs>

이렇게 구현 해 놓으면 게임 내부의 어느 곳에서든지 GameManager.instance 로 모두 접근 가능하고, 하나의 객체로 중복 생성 없이 객체를 유지할 수 있다.

또한 싱글턴 객체를 상속하여 유익한 구조를 만들 수도 있다. 다음은 C++ 코드로 PS3와 NDS 파일 시스템 구분을 싱글턴 객체를 상속하여 손쉽게 구현하는 방식이다.

이렇게 구현해 놓으면 다른 모든 클래스에서는 플랫폼의 구분을 할 필요 없이 FileSystem.instance 호출 만으로 플랫폼에 맞는 File System을 획득하게 된다.

이처럼 싱글턴은 여러모로 유용하게 사용할 수 있으나 의외로 싱글턴이 문제를 일으키는 경우도 많다. 그래서 많은 개발자들이 싱글턴의 사용을 즐겨하지만 또 많은 개발자들이 싱글턴 오용의 위험성을 경고한다.

왜냐하면, 싱글턴은 일종의 전역 변수이기 때문이다.
전역 변수이므로 전역 변수가 가지는 모든 장점과 단점을 다 가지고 있다. 모든 곳에서 접근 가능하니, 객체의 변경 시점과 변경 주체를 알기가 쉽지 않다. 호출하는 모든 코드들을 다 뒤져야 할 수 있다. 또한 전역 변수이기 때문에 여러 클래스와 Coupling이 된다. 하나의 코드를 수정했을 때 싱글턴과 연결된 다양한 곳에서 문제를 발생 시킬 수 있는 것이다. 가능한한 Instance 접근을 통제하는 것이 결과적으로 더 적은 버그를 만들게 한다. 또한 전역 변수이기 때문에 멀티 쓰레드의 환경에서는 문제가 발생한다. 모든 곳에서 접근 가능하기 때문에 race condition이 발생할 수 있다. 그걸 피하기 위해서 필연적으로 싱글턴에선 mutex lock과 unlock을 반복해서 걸게 되는게 자연히 코드 전체적으로 Performance가 떨어지는 문제가 발생한다.

Singleton의 대안으로는 가능한 Manager류의 생성을 피하고 각 클래스 안으로 코드를 집어 넣는 것이다. 또한 전역 접근이 불가능한 Singleton을 만드는 것도 방법이다. 대신 클래스끼리 서로 넘겨주고 받으면서 사용하거나, 상위 클래스로부터 얻거나, 이미 전역인 객체 안에 위치시켜서 접근 방식 자체를 줄이는 것도 생각해 볼 수 있다.