본문 바로가기
게임개발/유니티 엔진

싱글톤 패턴 적용

by do_ng 2024. 2. 2.

싱글톤 패턴이란?

단 하나의 유일한 인스턴스를 만들기 위한 디자인 패턴이며, 
메모리 절약을 위해 인스턴스를 매번 새로 만들지 않고 초기에 하나의 인스턴스만 만든 후 재사용하는 방식이다.

 

해당 인스턴스가 빈번하게 사용되는 경우에 싱글톤 패턴을 적용하면 좋다.

예시를 들자면,

Cat 인스턴스에서 어떠한 이벤트가 일어났을 때 GameManger 인스턴스가 처리를 하는데 게임이 시작되는 동안 Cat 인스턴스는 여러 개 생성되는데 그 처리를 담당하는 GameManger 인스턴스도 여러 개 생성된다면 수백 개, 수천 개의 Cat 인스턴스가 생성되는 경우 그에 따라서 GameManger 인스턴스도 많아지기 때문에 리소스를 많이 잡아먹게 된다.

그래서 전역으로 사용되는 인스턴스를 하나 만들고 각각의 Cat 오브젝트에서 어떠한 이벤트가 발생했을 때, 해당 함수를 통해서 유일한 전역 인스턴스에 접근하게 된다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
    private static GameManager gameManager;

    public static GameManager GetInstance() {        
        init();
        return gameManager; 
    }    

    void Start()
    {
        init();
    }

    static void init()
    {
        // 최초로 싱글톤으로 사용할 인스턴스를 만들때 해당 오브젝트에 연결되있는 스크립트가 컴포넌트화된 것(인스턴스)을 가져옴
        if(gameManager == null)
        {
            GameObject go = GameObject.Find("GameManager");
            // 해당 오브젝트가 없으면 직접 만들어줌
            if(go == null)
            {
                go = new GameObject { name = "GameManager" };
                go.AddComponent<GameManager>(); // "GameManager" 스크립트를 컴포넌트로 해당 오브젝트에 붙여줌
            }
            DontDestroyOnLoad(go);
            gameManager = go.GetComponent<GameManager>();
        }        
    }    

}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Cat : MonoBehaviour
{  
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.tag == "food")
        {            
            energy += 1.0f;
            if (energy < fullAmount)
            {                                
                gameObject.transform.Find("hungry/Canvas/front").transform.localScale = new Vector3(energy / fullAmount, 1, 0);            
                Destroy(collision.gameObject); // cat과 충돌한 food를 제거
            }
            else
            {
                if (isFull == false)
                {                    
                    if(bossAlive == true)
                    {
                        GameManager.GetInstance().gameClear(); // 게임 클리어                                                
                    }
                    else
                    {
                        GameManager.GetInstance().addLevelScore();
                        gameObject.transform.Find("hungry").gameObject.SetActive(false);
                        gameObject.transform.Find("full").gameObject.SetActive(true);
                        isFull = true;
                    }                    
                }
            }                   
        }

        if (collision.gameObject.tag == "fishShop")
        {
            if (energy < fullAmount)
            {
                GameManager.GetInstance().gameOver();
            }            
        }
    }

}

 

싱글톤 패턴의 단점

싱글톤 인스턴스가 너무 많은 기능을 담당해서 다른 인스턴스들과의 결합도가 높아지게 되는 경우가 있다.
여기서 해당 객체를 수정한다고 했을 때 단순히 해당 객체만 수정하는게 아니라 그 객체와 결합되어 있는 싱글톤 객체도 수정해야 하는 상황이 벌어진다면 좋은 설계로 보기 힘들다.
이러한 문제는 객체지향 설계 원칙 중에서 개방-폐쇄 원칙(OCP)인 객체 간의 의존성을 최소화하여 코드 변경에 따른 영향력을 낮추기 위한 것들을 위반하게 된다.

 

싱글톤으로 구현한 인스턴스는 전역이기 때문에 다른 클래스의 인스턴스들이 데이터를 공유할 수 있게 된다.
멀티 스레드 환경이라면 동시에 해당 인스턴스에 접근을 할 수 있다는 뜻인데 동기화 처리를 하지 않으면 인스턴스가 2개 이상 생성될 수 있는 문제가 발생할 수 있다.


 

참고 : https://blog.itcode.dev/posts/2021/08/14/open-closed-principle#%EC%BD%94%EB%93%9C%EB%A1%9C-%EB%B3%B4%EB%8A%94-%EA%B0%9C%EB%B0%A9-%ED%8F%90%EC%87%84-%EC%9B%90%EC%B9%99

 

참고 : https://gyoogle.dev/blog/design-pattern/Singleton%20Pattern.html