[GOF] 싱글톤(Singleton) 패턴

2024. 3. 16. 13:46디자인패턴

1. 싱글톤 패턴

  • 단 하나의 유일한 객체를 만들기 위한 코드 패턴
  • 메모리 절약을 위해, 인스턴스가 필요할때 똑같은 인스턴스를 새로 만들지 않고 재활용 하는 기법
  • 대표적으로 데이터베이스 연결 모듈
  • 이밖에도 디스크 연결, 네트워크 통신, DBCP 커넥션풀, 스레드풀, 캐시, 로그 기록 등에 이용된다.

  • 정적 메소드 getInstance()를 통해 객체를 불러와 변수에 저장하고 이를 출력해보면 똑같은 객체 주소를 가지고있다.
  • 객체 하나만 생성하고 여러변수에 불러와도 돌려쓰기를 한것
public class Main {
    public static void main(String[] args) {

        // Singleton.getInstance() 를 통해 싱글톤 객체를 각기 변수마다 받아와도 똑같은 객체 주소를 가리킴
        Singleton i1 = Singleton.getInstance();
        Singleton i2 = Singleton.getInstance();
        Singleton i3 = Singleton.getInstance();

        System.out.println(i1.toString()); // Singleton@1b6d3586
        System.out.println(i2.toString()); // Singleton@1b6d3586
        System.out.println(i3.toString()); // Singleton@1b6d3586

        System.out.println(i1 == i2); // true
    }
}

2. 검증된 싱글톤 구현 기법 종류

싱글톤 패턴을 구현하는 기법들은 여러가지 존재하지만, 권장되는 검증된 패턴은 2가지 있다.

  • Bill Pugh Solution
    • 권장되는 두가지 방법중 하나
    • 멀티쓰레드 환경에서 안전하고 Lazy Loading(나중에 객체 생성)도 가능한 완벽한 싱글톤 기법
    • 클래스 안에 내부 클래스를(holder)를 두어 JVM의 클래스 로더 매커니즘과 클래스가 로드되는 시점을 이용한 방법
    • static 메소드에서는 static 멤버만을 호출할 수 있기 때문에 내부 클래스를 static으로 설정
    • 다만 클라이언트가 임의로 싱글톤을 파괴할 수 있다는 단점을 지님 ( Reflection API, 직력화/역직렬화)
class Singleton {

    private Singleton() {}

    // static 내부 클래스를 이용
    // Holder로 만들어, 클래스가 메모리에 로드되지 않고 getInstance 메서드가 호출되어야 로드됨
    private static class SingleInstanceHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingleInstanceHolder.INSTANCE;
    }
}
  1. 내부클래스를 static으로 선언하였기 때문에, 싱글톤 클래스가 초기화 되더라도 SingleInstanceHolder 내부 클래스는 메모리에    로드되지 않음
  2. 어떠한 모듈에서 getInstance() 메소드를 호출할 때, SingleInstanceHolder 내부 클래스의 static 맴버를 가져와 리턴하게 되어  이때 내부 클래스가 한번만 초기화되면서 싱글톤 객체를 최초로 생성 및 리턴하게 된다.
  3. 마지막 final로 지정함으로서 다시 값이 할당되지 않도록 방지한다.
  • Enum 사용
    • 권장되는 두가지 방법중 하나
    • enum은 애초에 멤버를 만들때 private로 만들고 한번만 초기화 하기 때문에 thread safe함
    • enum 내에서 상수뿐만 아니라, 변수나 메소드를 선언해 사용이 가능하기 때문에, 이를 이용해 싱글톤 클래스처럼 응용
enum SingletonEnum {
    INSTANCE;

    private final Client dbClient;
	
    SingletonEnum() {
        dbClient = Database.getClient();
    }

    public static SingletonEnum getInstance() {
        return INSTANCE;
    }

    public Client getClient() {
        return dbClient;
    }
}

public class Main {
    public static void main(String[] args) {
        SingletonEnum singleton = SingletonEnum.getInstance();
        singleton.getClient();
    }
}

 

3. 싱글톤 패턴의 단점

  1. 모듈간 의존성이 높아진다.
    • 하나의 싱글톤 클래스를 여러모듈이 공유를 하니깐 인스턴스가 변경되면 참조하는 모듈들도 수정이 필요함
  2. S.O.L.I.D 원칙에 위배되는 사례가 많다.
    • 하나가 여러 책임을 지니는 경우가 많아 단일 책임원칙(SRP)을 위반
    •  결합도가 높아지게 되어 개방-페쇠 원칙(OCP)에 위배
    • 클라이언트가 추상화가 아닌, 구체 클래스에 의존하게 되어 의존역적 원칙(DIP)도 위반
    • 싱글톤 패턴을 객체지향 프로그래밍의 안티 패턴이라고 불리운다.
  3. TDD 단위 테스트에 애로사항이 있음

하지만,  스프링 컨테이너 같은 프레임워크의 도움을 받으면, 싱글톤 패턴의 문제점들을 보완하면서 장점의 혜택을 누릴 수 있다. ( 싱글톤 컨테이너)

 

'디자인패턴' 카테고리의 다른 글

[GOF] 추상 팩토리 패턴  (0) 2024.04.02
[GOF] 템플릿 메소드 패턴  (0) 2024.03.19
[GOF] 상태(State) 패턴  (0) 2024.03.18
[GOF] 전략(Strategy) 패턴  (0) 2024.03.17