싱글톤
1.정의
싱글턴 패턴은 객체를 하나의 인스턴스로 제한하여, 메모리 사용을 줄이고 객체의 생성과 소멸을 제어할 수 있는 디자인 패턴
다른 어떤 클래스도 자신의 인스턴스를 추가로 만들지 못하게 해야한다.
인스턴스에 접근할 수 있도록 전역 접근자를 제공해야한다.
2.장점
- 인스턴스를 하나만 생성하기 때문에 메모리를 효율적으로 사용할 수 있습니다.
- 다중 스레드 환경에서도 안정적으로 동작합니다.
- 객체의 생성과 소멸을 단일한 위치에서 관리하기 때문에 유지보수가 용이합니다.
3.예제
3.1.기본 싱글턴
public static HammerUI Instance { get; private set; }
private void Awake()
{
Instance = this;
}
장점은 구현하기 쉽다는 것이다. 간단한 예제같은 경우 많이 쓴다! 또한 대부분의 매니저같은 경우 게임오브젝트가 항상 Active 상태이기 때문에 가끔 사용하는 경우도 있다.
하지만 단점이 상당하다. 만약 먼저 호출된 다른 클래스의 Awake에서 싱글턴이 호출될 경우 NullException이기에 Exception이 발생할 것이다. 즉 호출 순서에 관계없이 사용하는데에는 부적합하다.
+저 싱글턴은 상속이 가능하다. => sealed 키워드를 이용해서 상속을 막을 필요가 있을 것이다.
+생성자를 호출이 가능함으로 다른 클래스에서 싱글턴 클래스를 인스턴스화 할 수 있다. => 생성자를 private화
+게임오브젝트가 존재하지 않는다면 ? Awake는 호출될 일이 없어서 Instance는 평생 null 일 것이다 => 게임오브젝트가 없다면 생성해서 만들어준다.
+모노비헤이버를 상속받지 않는다면 ? 위와 같은 맥락이긴 한데 경우가 달라서 넣었다. 모노비헤이버가 없으면 Awake도 존재하지 않기에 저 코드는 적용되지 않는다.
3-2 개선 싱글턴
public sealed class GameManager : MonoBehaviour //상속 불가능하게 sealed
{
private static GameManager _instance;
private GameManager() //생성자를 private으로 선언하여 내부에서는 생성이 가능하고, 외부에서는 접근 불가능하다.
{
//초기화 구문
}
public static GameManager Instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType<GameManager>();
}
return _instance;
}
}
}
4.일반화를 이용하여 사용이 쉽게 만들기
위와같은 패턴을 싱글턴을 이용할 때마다 클래스마다 일일히 타이핑 하는것은 비효율적일것이다.
namespace SingletonPattern
{
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static readonly object _lock = new object();
private static T _instance;
public static T Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
_instance = (T)FindObjectOfType(typeof(T));
if (_instance == null)
{
GameObject obj = new GameObject();
_instance = obj.AddComponent(typeof(T)) as T;
obj.name = typeof(T).ToString();
//모든 Scene에서 살아 있어야 될 때 활성화.
DontDestroyOnLoad(obj);
}
}
}
return _instance;
}
}
}
}
를
//상속을 구현함으로 싱글턴 패턴 구현완료
public sealed class GameManager : Singleton<GameManager> //sealed 키워드로 상속방지
{
private GameManager() {} //생성자 private해서 새로운 인스턴스 생성 금지
}
이런식으로 사용하면 된다.
제네릭 싱글턴은 아래의 링크에서 참고했다.
+다만 몇가지 수정점이 있다.
1.멀티스레딩 환경에서 동시에 Singleton 객체에 접근하는 것을 막기 위해 lock을 사용
2.GameObject를 만들 때, GameObject를 생성하고, 컴포넌트를 추가하고, 이름을 설정하는 작업을 한 번에 처리
5.결론
- 전역 상태를 유지한다: 싱글턴 객체는 어디서나 접근할 수 있기 때문에, 전역 상태를 유지하게 됩니다. 이는 예측하지 못한 부작용을 일으킬 수 있습니다.
- 상속이 어렵다: 싱글턴 객체는 생성자가 private으로 선언되기 때문에, 다른 클래스가 상속받아서 확장하는 것이 어렵습니다.
- 단위 테스트에 어렵다: 싱글턴 객체는 전역 상태를 유지하고 다른 객체에 대한 참조를 보유하기 때문에, 단위 테스트를 수행하기 어렵습니다.
- 멀티스레딩 문제가 발생할 수 있다: 멀티스레드 환경에서 동시에 싱글턴 객체에 접근하면, 경합 상태(race condition)가 발생할 수 있습니다. 이를 해결하기 위해서는 동기화 처리를 해주어야 합니다.
- 메모리 누수가 발생할 수 있다: 싱글턴 객체는 프로그램이 종료될 때까지 메모리를 차지하고 있기 때문에, 메모리 누수가 발생할 수 있습니다.
따라서 사용하기 쉽다고 남발하지 말고 꼭 필요한 클래스에만 구현하길 바란다.
'게임공부 > 디자인패턴' 카테고리의 다른 글
[디자인패턴] Flyweight Pattern 경량패턴 (0) | 2023.05.18 |
---|---|
[디자인 패턴] 객체 지향 디자인 패턴 1 (1) | 2023.05.15 |
객체 지향 설계 5대 원칙 SOLID (1) | 2023.05.14 |
[디자인 패턴] 디자인 패턴 개요 (0) | 2023.05.12 |