본문 바로가기

디자인 패턴

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

싱글톤 패턴

하나의 인스턴스만 생성하여 여러 객체가 한 인스턴스에만 접근이 가능하게 만든 패턴이다. 유니티로 메타버스 개발시 캐릭터가 맵을 이동할 때 마다 새로운 캐릭터 인스턴스가 생성되어 맵 이동시마다 캐릭터의 수가 두배가 되었는데 이를 싱글톤 패턴을 사용하여 해결했던 경험이 있다. 이뿐만 아니라 여러 곳에서 하나의 자원을 공유해야 하는 상황에서 싱글톤 패턴을 자주 사용한다. (캐시, 커넥션풀, 스레드풀 등)

 

구현

public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton(){} // 기본 생성자를 private로 선언해서 새로운 인스턴스가 생성되지 못하게 한다.

    public static Singleton getInstance(){
        return instance;
    }
}

가장 기본적인 싱글톤 패턴이다. 멀티쓰레드 환경에서 동시에 인스턴스에 접근해도 문제가 발생하지 않지만 인스턴스를 사용하지 않아도 인스턴스가 생성되어 메모리 낭비가 발생할 수 있다.

public class Singleton {
    private static Singleton instance;
    private Singleton(){} // 기본 생성자를 private로 선언해서 새로운 인스턴스가 생성되지 못하게 한다.

    public static Singleton getInstance(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

그럴땐 이렇게 인스턴스가 처음 호출될 때 인스턴스를 생성해주는 방식으로 해결이 가능하다. 하지만 이 방법은 인스턴스가 생성되지 않았을 때 동시에 인스턴스에 접근하면 문제가 발생한다.

public class Singleton {
    private Singleton(){} // 기본 생성자를 private로 선언해서 새로운 인스턴스가 생성되지 못하게 한다.

    // getInstance() 호출시 싱글톤 인스턴스 생성
    private static class LazyHolder{ //내부 클래스가 static으로 선언되어 여러번 호출되지 않는다.
        private static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance(){
        return LazyHolder.instance;
    }
}

이렇게 하면 멀티쓰레드 문제도 해결 가능하다. synchronized 키워드를 이용해 해결하는 방법도 있으나 성능상의 이슈가 있어 이 방법이 현재 가장 좋은 방법으로 통용된다.

 

장점

  • 인스턴스를 여러개 생성하지 않는 점에서는 메모리의 효율이 좋다.
  • 이미 생성된 인스턴스를 가져다 쓰는 것이기 때문에 성능이 개선된다.
  • 전역변수처럼 프로그램 전체에서 싱글톤 클래스를 공유할 수 있다.

단점

  • 다른 클래스들과의 결합도가 커져 개방-폐쇄 원칙에 위배될 가능성이 크다.
  • 단위 테스트가 힘들다.
  • 한번 인스턴스가 생성되면 인스턴스가 사용되던 안되던 프로그램이 끝나기 전까지 메모리에 남게 되어 메모리 낭비가 발생한다.
  • 멀티 쓰레드 환경에서 별다른 처리를 해주지 않으면 인스턴스 재할당 문제가 발생할 수 있다.