Blog
컴퓨터 공학
소프트웨어 아키텍쳐
디자인 패턴

싱글톤 패턴

하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴이다.

  • 장점 : IO 바운드 작업(DB 연결, 인터넷 연결 등)을 할 때 많이 쓰이며 인스턴스 생성에 들어가는 비용을 줄일 수 있다는 장점이 있다.
  • 단점 : TDD할 때 테스트가 어려워지며 의존성이 높아진다는 단점이 있다.

의존성이 강한이유

GPT : Singleton 클래스는 전역 접근이 가능하므로 코드 어디에서나 그 인스턴스를 가져와 사용할 수 있습니다. 이는 다양한 클래스들이 Singleton 인스턴스에 의존하게 되고, 그 결과로 이들 클래스들은 느슨하게 결합된 코드보다 훨씬 강하게 결합됩니다.

public class Singleton {
    private static Singleton instance;
    
    private Singleton() {}
 
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
 
    public void doSomething() {
        System.out.println("Doing something...");
    }
}
 
public class Client {
    public void doWork() {
        Singleton singleton = Singleton.getInstance();
        singleton.doSomething();
    }
}

해결방안

GPT : 이를 해결하기 위한 한 가지 방법은 의존성 주입입니다. 즉, Client 클래스가 Singleton 클래스의 인스턴스를 직접 가져오지 않고, 대신 Client 클래스의 생성자나 메소드를 통해 외부에서 Singleton 인스턴스를 전달받도록 하는 것입니다. 이렇게 하면 Client 클래스를 테스트할 때 Singleton 클래스의 실제 인스턴스 대신 가짜(mock) 인스턴스를 전달할 수 있어, Client 클래스의 독립적인 테스트가 가능해집니다. 이 방법은 코드의 결합도를 줄이고, 테스트 용이성을 높이는 효과가 있습니다.

Singleton 패턴의 구현

class Singleton {
  static final Singleton _singleton = Singleton._internal();
 
  // 인스턴스를 새로 생성하지 않는 생성자
  factory Singleton() {
    return _singleton;
  }
 
  // 클래스 내부 생성자 _singleton을 초기화할 때만 호출
  Singleton._internal();
}
void main() {
  var s1 = Singleton();
 
  var s2 = Singleton();
  print("${s1.hashCode}\n${s2.hashCode}"); // 같은 주소 출력
}

Java Singleton

// LazyHolderSingleTon클래스가 최초에 로딩되더라도 함께 초기화가 되지 않고
// getInstance()가 호출될 때 singleInstanceHolder.INSTANCE가 초기화가 된다.
class LazyHolderSingleTon {
    private static class singleInstanceHolder {
        private static final LazyHolderSingleTon INSTANCE = new LazyHolderSingleTon();
    }
 
    public static LazyHolderSingleTon getInstance() {
        return singleInstanceHolder.INSTANCE;
    }
}