Singleton
실제로 생성되는 객체가 하나 (한 번만 메모리 할당)
사용하는 경우
- 객체 구별 필요 없는 경우 (수정 가능한 멤버 변수 없이 기능만 있는 경우)
- stateless한 객체
- 객체 재생성/삭제에 많은 비용이 들어 재사용이 유리한 경우
- 프로그램 안에 해당 객체가 단 한 개만 있어야 할 때
- 여러 부분에서 해당 객체 공유할 때
이벤트 스케쥴링/처리 객체
기본 적용
- 외부에서 생성자 접근 금지 → 생성자 private
- 내부에서 직접 객체 생성 → 멤버 변수, private
- 외부에서 접근 가능하도록 getter 생성 (setter 불필요)
- 객체 없이 외부 접근 가능하도록 getter, 변수에 static 추가
→ 외부에서는 언제나 getter 통해 객체 참조, 하나의 객체 재사용함
장점
- 메모리 절약 가능
- 데이터 공유 쉬움
- 도메인 관점에서 인스턴스 단 한 개인 것을 보증 가능
문제점
- 멀티스레딩 환경에서 동시성 문제 발생 가능함 → syncronized 사용 필요
- 테스트가 어려움 (공유되고 있어서 항상 상태 초기화 해줘야 함)
- 클라이언트가 구체 클래스에 의존함
- 자식 클래스 만들 수 없음
- 내부 상태 변경이 어려움
→ 적절한 trade-off 필요
구현 기법
- LazyHolder
- 멀티쓰레드 환경에서 안전
- 필요 시 생성 가능 (Lazy Loading)
- static Inner class (Holder)를 두어 생성 → JVM 클래스 로더 방식과 클래스 로딩 시점을 이용
inner class는 외부 클래스 초기화 시에 초기화 안됨 , static 변수는 클래스 로딩 시 초기화됨 - JVM에 객체 생성(초기화) 맡김
- getInstance 호출 시 inner class 로드됨
- getInstance 호출 → Holder의 static final인 INSTANCE 반환 (이때 최초로 한 번 실행 후 다시 초기화되지 않음)
- User로 인해 싱글톤 파괴될 수 있음 (ReflectinoApi, 직렬/역직렬화)
class Singleton {
private Singleton() {}
private static class SingleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingleInstanceHolder.INSTANCE;
}
}
//출처: https://inpa.tistory.com/entry/GOF-💠-싱글톤Singleton-패턴-꼼꼼하게-알아보자 [Inpa Dev 👨💻:티스토리]
- Enum
- 멤버 만들 때 private로 만들고, 한 번만 초기화됨
- 클라이언트에서 Reflection 공격에도 안전함
- enum 외의 클래스 상속 불가능
- 멀티톤으로 마이그레이션 시 다시 개발해야함
스프링에서는 컨테이너가 알아서 관리해줘서 따로 싱글톤 구현할 일 거의 없음!
참고 블로그
https://inpa.tistory.com/entry/GOF-💠-싱글톤Singleton-패턴-꼼꼼하게-알아보자