Singleton Pattern

최진규·2023년 9월 30일
0

Design Patterns

목록 보기
1/2
  • 생성 패턴 중에 하나
  • 하나의 instance만을 가질 수 있도록 보장한다.
  • global한 access point를 제공한다.

Problems

singleton은 두가지 문제를 해결한다.
1. 클래스가 하나의 인스턴스만을 가진다.

  • 왜 클래스가 가지는 인스턴스의 숫자가 중요할까 ? 가장 대표적인 문제는 공유되는 자원이다. shared resource라고 하는데, 예를 들어서 DB나 file을 의미한다.
  • 하나의 객체를 생성했는데, 조금 있다가 새로운 객체를 생성한다고 가정하자. 이 경우 새로운 객체를 얻는것이 아니라 이전에 만들어진 객체를 가져온다.
  • 이런 패턴은 기존의 생성 방식으로는 불가능하다. 생성자는 항상 새로운 객체를 반환하기 때문이다.
  1. 인스턴스의 global access point를 제공한다.
  • 예를 들어서 global한 변수에 중요한 객체를 담는다고 가정하자. 이 변수를 다루는것은 편리하지만, 동시에 굉장히 unsafe하다. 중요한 객체를 담고 있는 변수가 접근가능하며, 이 경우 쉽게 변경이 가능하기 때문이다. 쉬운 변경은 어플리케이션 자체를 위험에 빠뜨린다.
  • global variable처럼 singleton pattern은 어디서든지 접근할 수 있지만, 이 경우 singleton pattern으로 생성된 변수는 다른 조작에 의해서 담고 있는 객체 또는 변수가 변경되지 않는다.

Solutions

singleton을 구현하는 방법은 공통적으로 두가지 step이 있다.

  1. default constructor를 private으로 한다.
    • 이는 코드 다른 곳에서 new 로 새로운 객체를 생성하는 것을 방지한다.
  2. 생성자의 역할을 하는 static한 method를 만든다.
    • 이는 private constructor를 호출한다.
    • 생성자의 번환값을 static field에 저장한다.
    • 추가적인 호출의 반환값은 필드에 저장된 캐시된 객체를 반환한다.

현실 적용

정부는 singleton pattern의 가장 딱 들어맞는 예시이다.
국가는 한개의 정부를 가진다.
정부를 구성하는 개인들과 무관하고 특정 국가의 정부라는 말은 정부에 대한 global한 point가 된다.

구조

  1. getInstance라는 static한 method를 가진다. 이 method가 global한 entry point가 된다.
  2. constructor는 private이여서 밖에서 접근할 수 없다.

구현

not thread safe

public class Singleton {

  private static Singleton instance;
  public String value;

  private Singleton(String value) {
  	this.value = value;
  }
  
  public static Singleton getInstance() {

    if (Objects.isNull(instance)) {
      instance = new Singleton();
    }
    return instance;
  }
}

thread safe

public class Singleton {

  private static volatile Singleton instance;
  public String value;


  private Singleton(String value) {
    this.value = value;
  }

  /**
   * DCL(double checked-lock)이 적용되어 race condition을 방지한다.
   * @param value
   * @return
   */
  public static Singleton getInstance(String value) {
    
    Singleton result = instance;

    if (Objects.nonNull(result)) {
      return result;
    }

    synchronized (Singleton.class) {
      if (Objects.isNull(instance)) {
        instance = new Singleton(value);
      }
      return instance;
    }
  }
}

참고

singleton instance를 얻는 getInstance는 thread safe해야 하기 때문에, mutex lock이 추가될 필요가 있다.
멀티 스레딩 환경에서 thread safe하지 않으면 getInstance가 반복해서 생성자를 호출하게 될 수 있다.

추가로 singleton에서 lock을 획득하는데는 double checked locking이 적용되면 성능이 훨씬 상향된다.

적용

  • 클래스의 instance가 하나임이 보장되어야 할때,
  • global한 access point가 필요하지만 global variable보다 엄격해야 할때,
  • 그러면서 내부의 값이 변경되면 안되는 경우.

장단점

장점

  1. single instance를 보장한다.
  2. global access point를 갖는다.
  3. 첫 요청에서만 초기화된다.

단점

  1. single responsibility principle를 위반한다.
    1. 이 패턴은 두개의 문제를 동시에 해결한다.
  2. program의 component들이 의존성이 있거나 서로에 대해서 너무 잘아는 경우 안좋은 패턴이 된다.
  3. 멀티 스레딩 환경에서는 특수한 코드가 더 필요하다. 뮤텍스 락 같은 코드가...
  4. 테스트가 힘들다.
    1. 많은 테스트 프레임워크가 모킹시에 상속에 의존하고 있다. 생성자가 private이고, static method를 override하는게 불가능하기 때문에, 모킹이 어려울 수 있다.
profile
개발하는 개복치

0개의 댓글

Powered by GraphCDN, the GraphQL CDN