*출처 : refactoring.guru(https://refactoring.guru/ko/design-patterns/singleton)클래스에 인스턴스가 하나만 있도록 하면서 이 인스턴스에 대한 전역 접근(액세스) 지점을 제공하는 생성 디자인 패턴입니다.
전역적으로 사용되는 객체(Object)를 매번 new 연산자로 새로운 객체를 생성하면 일관성이 깨지게 된다.
이런 객체를 전역적(static)으로 선언해주고 객체가 없을 경우 하나의 객체를 생성하고,
이미 생성된 하나의 객체만을 사용하는 패턴이다.
위에도 언급했듯이 객체(Object)를 최초 생성한 것을 재사용 하면서 일관성을 보장하게 해준다.
그리고 자주 호출되는 객체를 new 연산자로 매번 생성하면 메모리 누수의 문제를 방지 할 수 있다.
메모리 누수의 문제도 크겠지만 최초 생성한 동일한 객체를 사용하기 위한 이유가 더 큰 것 같습니다.
C# Unity를 공부하면서 싱글턴 패턴의 필요성을 좀 더 직관적으로 이해가 되었습니다.
주인공(스파이더맨)은 하나인데 매번 생성이 된다면 큰 문제가 될 것입니다.
그리고 생성된 주인공들의 상태를 변경 할 때도 서로 다른 값을 수정할 수 있기 때문에 문제가 발생할 수 있습니다.
첫 번째 주인공 객체에서 HP가 10이 줄어들었습니다.
두 번째 주인공 객체에서 힐링 포션을 먹어서 HP가 10 추가되었습니다.
게임에서 주인공이 분신술을 사용하지 않는 이상 발생하면 안되는 상황인 것입니다.
싱글턴 패턴의 필요성과 개념은 알았으니 이번에는 단점이 무엇이 있는지 찾아보고 고민해봤습니다.
Race condition
문제 발생 가능성이 증가한다.mutex lock, unlock
을 반복적으로 걸기 때문에 코드의 성능이 떨어지게 된다.싱글턴 패턴이 최초 하나의 객체만을 생성하기 때문에 성능적으로도 보장이 되는 줄 알았습니다.
단점을 공부하면서 오히려 멀티 스레드 환경이나 여러 곳에서 호출되는 싱글턴 객체는 성능 이슈를 발생할 수 있다는 것을 알게되었습니다.
namespace SingletonStudy
{
class Player
{
private Player() { }
// (1) Player 인스턴스
private static Player instance;
// (2) 외부에서 싱글턴 Player 객체를 Get 하는 함수
public static Player GetInstance()
{
if (instance == null)
{
instance = new Player();
}
return instance;
}
}
class Program
{
static void Main()
{
// (3) Player1과 Player2 객체에 싱글턴 Player 인스턴스 주입
Player player1 = Player.GetInstance();
Player player2 = Player.GetInstance();
// (4) Player1과 Player2가 같은 객체인지 검증
if (player1 == player2)
{
Console.WriteLine("player1과 player2는 같은 객체");
}
}
}
}
싱글턴 패턴 코드를 생성자를 사용해도 동일한 인스턴스가 생성 되도록 하는 방법도 있습니다.
하지만 간단하게 코드를 보고 이해하기 위해 위 예제코드를 가져와 봤습니다.
public class Managers : MonoBehaviour
{
static Managers s_instance;
public static Managers Instance() { init(); return s_instance; }
// Start is called before the first frame update
void Start()
{
init();
}
// Update is called once per frame
void Update()
{
}
static void init()
{
if (s_instance == null)
{
GameObject go = GameObject.Find("@Managers");
// 게임 오브젝트가 null이면 @Managers GameObject 생성 및 추가
if (go == null)
{
go = new GameObject { name = "@Managers" };
go.AddComponent<Managers>();
}
// 게임 오브젝트가 파괴되지 않도록 설정
DontDestroyOnLoad(go);
// 게임 오브젝트의 Managers 컴포넌트를 인스턴스에 추가
s_instance = go.AddComponent<Managers>();
}
}
}
위 예제 코드는 인프런 'C#과 유니티로 만드는 MMORPG 게임 개발 시리즈' 강의 코드를 가져온 것입니다.
구조는 어느정도 비슷하지만, C# 유니티에서는 GameObject 클래스를 사용해서 싱글턴 패턴을 구현합니다.
아직 강의에 초입 부분이라 싱글턴 게임 오브젝트를 제대로 활용해 보지는 못했습니다.
하지만 개념을 어느정도 잡고 가야 할 것 같아서 이렇게 작성해봤습니다.
다양한 레퍼런스를 읽고 정리하면서 싱글턴의 개념, 사용 이유, 단점들을 알게 되었습니다.