Deff_Dev

[디자인 패턴] 싱글톤 패턴 (Singleton Pattern) 본문

CS/디자인 패턴

[디자인 패턴] 싱글톤 패턴 (Singleton Pattern)

Deff_a 2024. 4. 7. 00:51

싱글톤 패턴이란 ?

  • 게임 상이나 메모리 상으로 단 하나만 존재하며 언제 어디서든 사용 가능한 오브젝트를 만들 때 사용된다.
  • 유니티에서는 오디오 관리, 데이터 관리, 설정 관리 등등 게임 내에서 전반적으로 사용, 관리되어야 할 때 싱글톤 패턴을 사용하면 유용하다.

싱글톤 패턴 특징

  • 접근성 : 스크립트의 접근성이 높아진다.
  • 유일성 : 기능 별로 단 하나만 유일하게 존재해야한다.

기본적인 사용법

using UnityEngine;
public class GameManager : MonoBehaviour
{
    // static으로 선언해 해당 인스턴스를 접근 가능하게 함
    public static GameManager instance =  null;

    private void Awake()
    {
        if (instance == null) // instance가 null이라면
        {
            instance = this; // instance에 자기 자신을 저장함
            
            DontDestroyOnLoad(this.gameObject); // 씬이 넘어가도 해당 오브젝트가 사라지지 않게함
        }
        else
        {
            Destroy(this.gameObject); // 이미 인스턴스를 생성했다면 Destroy
        }
    }
}

 

싱글톤 패턴 단점

  • static을 사용하여 선언하기 때문에 게임을 종료할 때까지 메모리를 점유하므로 메모리 누수가 발생할 수 있다.
  • 싱글톤은 전역 상태를 유지하므로 코드의 복잡성을 증가시킬 수 있고 유지보수가 힘들어 질 수도 있다.

→ 편리하다고 많이 사용하면 치명적인 오류가 발생할 수 있으니 꼭 필요한 기능만 최소화해서 사용해야 한다.

 

 

위의 코드처럼 싱글톤 객체를 생성하는 방식은 매 스크립트마다 중복해서 선언해야 하므로 비효율적이다.
그래서 제네릭을 이용하여 싱글톤 클래스를 만들고 해당 클래스를 상속하는 방식으로 효율적으로 싱글톤 객체를 생성할 수 있다.

 

제네릭을 이용한 싱글톤 생성 예

using UnityEngine;

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance;

    public static T Instance
    {
        get
        {
            if(instance == null)
            {
                // 해당 컴포넌트를 가지고 있는 게임 오브젝트를 찾아서 반환한다.
                instance = (T)FindAnyObjectByType(typeof(T));

                if (instance == null) // 인스턴스를 찾지 못한 경우
                {
                    // 새로운 게임 오브젝트를 생성하여 해당 컴포넌트를 추가한다.
                    GameObject obj = new GameObject(typeof(T).Name, typeof(T));
                    // 생성된 게임 오브젝트에서 해당 컴포넌트를 instance에 저장한다.
                    instance = obj.GetComponent<T>();
                }
            }

            return instance;
        }
    }

    private void Awake()
    {
        if(transform.parent != null && transform.root != null) // 해당 오브젝트가 자식 오브젝트라면
        {
            DontDestroyOnLoad(this.transform.root.gameObject); // 부모 오브젝트를 DontDestroyOnLoad 처리
        }
        else
        {
            DontDestroyOnLoad(this.gameObject); // 해당 오브젝트가 최 상위 오브젝트라면 자신을 DontDestroyOnLoad 처리
        }
    }
}
using UnityEngine;

// Singleton 클래스를 상속하면 쉽게 인스턴스를 생성할 수 있다.
public class GameManager : Singleton<GameManager>  
{
    public void GameStart()
    {
        Debug.Log("게임 시작");
    }
}

 

하나의 싱글톤 인스턴스에 너무 많은 기능과 데이터를 넣으면 객체지향 설계 원칙인 SRP(Single Responsibility Principle)와 OCP(Open-Closed Principle)를 어길 수 있고, 이는 유지보수에 치명적일 수 있다.

따라서 싱글톤을 사용할 때에는 해당 인스턴스의 의존도를 낮추고, 한 가지 역할만 수행하도록 설계해야한다.

 

객체지향 설계 원칙(SOLID)

단일 책임의 원칙 (SRP)
(Single Responsibility Principle)
하나의 클래스는 하나의 목적을 위해 생성한다는 원칙
개방 폐쇄 원칙 (OCP)
(Open Close Principle)
소프트웨어의 구성요소는 확장에는 열려있고, 변경에는 닫혀있어야 한다는 원칙
리스코프 치환의 원칙 (LSP)
(Liskov Subsitution)
서브 타입(상속받은 하위 클래스)은 어디서나 자신의 기반 타입(상위 클래스)으로 교체할 수 있어야 한다는 원칙
인터페이스 분리의 원칙 (ISP)
(Interface Segregation Principle)
클라이언트는 자신이 사용하지 않는 메서드와 의존관계를 맺으면 안되고, 클라이언트가 사용하지 않는 인터페이스 때문에 영향을 받아서는 안된다는 원칙
의존성 역전의 원칙 (DIP)
(Dependency Inversion Principle)
실제 사용 관계는 바뀌지 않으며, 추상을 매개로 메시지를 주고받음으로써 관계를 최대한 느슨하게 만드는 원칙

 

 

참고자료

https://www.youtube.com/watch?v=a5TCCQgdv-E&t=622s

 

[유니티/공부] 유니티 주로 사용되는 싱글톤 패턴(Singleton Pattern) 정리!

| 싱글톤 패턴(Singleton Pattern) 소개 💬 정리할 싱글턴 패턴은 필요에 의해 하나의 클래스에서 딱 한 개의 인스턴스만 생성하게 해주는 디자인 패턴입니다. 다른 클래스에서 객체를 별도로 전역

devvdevv.tistory.com

 

스스로 공부하면서 정리한 내용으로 틀린 내용이 있을 수도 있습니다. 고쳐야할 점이 있다면 댓글 달아주세요 !