싱글톤 패턴
- 두 가지 목적을 가진 패턴
- 인스턴스를 오직 한 개만 생성
- 만들어진 하나의 인스턴스에 글로벌하게 접근할 수 있는 방법을 제공
싱글톤 패턴 구현 방법
싱글톤 패턴은 여러 가지 방법으로 구현 가능하며 각각의 특징에 대해 간략하게 설명해 보겠습니다.
1. 싱글톤 패턴 구현 방법 #1
- 동기화를 사용해 멀티쓰레드 환경에서 안전하게 만드는 방법
부연 설명
- 클래스 외부에서 생성자를 호출할 수 없도록 private 생성자를 선언하여 새로운 인스턴스 생성 불가능하도록 처리
- 싱글톤 패턴 목적에 맞게 클래스 내에서 인스턴스를 만들고 글로벌하게 접근할 수 있도록 처리해야 함
- 글로벌하게 접근할 수 있어야 하므로 정적(static) 메서드로 선언
- 멀티 쓰레드 환경에서도 thread-safe 하도록 synchronized 키워드를 getInstance() 메서드에 붙여 클래스락을 획득한 상태에서만 인스턴스를 반환받을 수 있도록 처리
- 단, 락 획득을 하는 과정에서 성능 저하 유발할 수 있음
2. 싱글톤 패턴 구현 방법 #2
- Eager Initialization을 사용하는 방법
부연 설명
- 여러 쓰레드가 접근해도 단순히 미리 생성한 인스턴스를 반환하므로 thread-safe
- 인스턴스 생성 비용이 상대적으로 비싸지 않을 경우 eager initialization 적용
- 인스턴스 생성 비용이 비쌀 경우 미리 생성한다는 것 그 자체가 단점이 될 수 있음
- 여기서 생성 비용이 비싸다는 생성 하는데 오래 걸리고 메모리를 많이 사용하는 등 리소스 소모량이 크다는 것
3. 싱글톤 패턴 구현 방법 #3
- double checked locking 기법 적용
부연 설명
- 자바 1.5 버전부터 동작하는 코드
- volatile 키워드를 붙여야 하는데 이는 instance가 생성됐는지 확인하는 조건문 자체는 동기화 블록 내 포함되지 않으므로 instance를 항상 캐시가 아닌 메모리로부터 불러와야 하기 때문
- 1번 방법을 보완한 코드
- 메서드 자체를 동기화하는 것은 비용이 크므로
- 인스턴스가 생성되지 않은 시점에 다수의 쓰레드가 동시에 if문을 통과하더라도 동기화 블록에서 한 번 더 체크하므로 thread-safe
- instance가 null일 때만 synchronized 구문을 접근하므로 1번 방법에 비해 성능 상 우위
- 인스턴스를 필요로 하는 시점에 만들 수 있다는 것 또한 장점
4. 싱글톤 패턴 구현 방법 #4
- static inner 클래스를 사용하는 방법
부연 설명
- getInstance가 호출될 때 LazySettingsHolder가 로딩되고 그때 Instance가 만들어져 Lazy Loading 되므로 static final 인스턴스를 선언했는데도 불구하고 지연 초기화 기법
5. 싱글톤 패턴 구현 방법 #5
- enum 클래스를 사용하는 방법
부연 설명
- 후술 할 "싱글톤 패턴을 깨트리는 방법"으로부터 안전한 클래스로 가장 안정적으로 싱글톤 패턴을 구현할 수 있는 방법
- 로딩하는 순간 인스턴스 자체가 미리 생성된다는 단점과 상속을 사용하지 못한다는 단점 존재
싱글톤 패턴 구현 깨트리는 방법
개발자가 제대로 thread-safe 하게 클래스를 설계하더라도 사용하는 클라이언트 단에서 두 가지 방법으로 깨트릴 수 있습니다.
1. 싱글톤 패턴 구현 깨트리는 방법 #1
- 리플렉션 사용하는 방법
- 리플렉션 관해서는 이펙티브 자바 아이템 65 참고
부연 설명
- 리플렉션 기능을 이용하면 프로그램에서 임의의 클래스에 접근할 수 있으며 Class 객체가 주어지면 클래스 정보를 통해 생성자 시그니처, 메서드 시그니처, 그리고 필드 타입, 멤버 필드 명 등을 가져올 수 있음
- 생성자 인스턴스를 통해 객체를 생성할 수 있음
- 메서드 인스턴스를 통해 메서드를 실행시킬 수 있음
- 싱글톤 패턴을 적용했기 때문에 private 생성자로 선언됐고 싱글톤 패턴을 깨트리기 위해서는 해당 생성자를 접근하고 호출해 새로운 인스턴스를 생성해야 하므로 setAcessible(true);
- enum 클래스의 경우 리플렉션으로도 싱글톤 패턴이 파훼가 안됨
- enum 클래스의 생성자를 접근하려고 시도하면 Cannot reflectivley create enum objects 메시지가 뜸
2. 싱글톤 패턴 구현 깨트리는 방법 #2
- 직렬화 & 역직렬화를 사용하는 방법
- 직렬화를 적용하기 위해서는 클래스가 마커 인터페이스인 Serializable 구현체여야 함
부연 설명
- 이펙티브 자바 아이템 89에서도 언급했다시피 Serializable를 구현하는 순간부터 싱글톤 패턴이 아님
- 역직렬화할 때 readObject() 메서드가 호출되고 어떤 readObject() 메서드를 선언하든 해당 클래스가 초기화될 때 만들어진 인스턴스와는 별개인 인스턴스를 반환하기 때문
- 단, readResolve() 메서드를 재정의하면 readObject()가 생성한 인스턴스를 다른 것으로 대체 가능하고 역직렬화한 객체의 클래스가 readResolve() 메서드를 적절히 구현했다면 역직렬화 후 새로 생성된 객체를 인수로 해당 메서드가 호출되고 해당 메서드가 반환한 객체 참조가 새로 생성된 객체를 대신 반환
* enum의 경우 별도 처리 없어도 직렬화/역직렬화로부터 안전함
실무에서 쓰이는 싱글톤 패턴
1. 자바의 Runtime
부연 설명
- currentRuntime 인스턴스를 미리 생성하고 getRuntime() 정적 메서드를 통해서만 인스턴스를 반환받도록 구현됨
- 외부에서 인스턴스를 생성할 수 없도록 private 생성자 선언
2. 스프링 프레임워크의 싱글톤 스코프 (singleton scope)
- 기본 빈(scope) 스코프로, 스프링 IoC 컨테이너(ApplicationContext)가 시작될 때 한 번 생성된 빈 인스턴스를 여러 곳에서 공유하도록 설정하며 애플리케이션 전체에서 해당 빈의 단일 인스턴스가 존재하도록 함
- 컨테이너가 시작될 때, 정의된 빈들이 한 번 생성
- 각 요청에서 동일한 빈 인스턴스를 반환
- 애플리케이션 종료 시점까지 해당 빈 인스턴스는 동일하게 유지
2.1 스프링 싱글톤 스코프 vs 싱글톤 패턴
생성 시점
- 스프링 싱글톤 스코프: 스프링 컨테이너가 시작될 때 빈이 초기화되고, 이후 모든 요청에서 동일한 빈을 반환
- 싱글톤 패턴: 클래스의 인스턴스가 처음 요청될 때 생성되며, 이후 동일한 인스턴스를 반환
관리 주체
- 스프링 싱글톤 스코프: 스프링 컨테이너가 빈의 생명주기를 관리하며 스프링 컨테이너가 초기화되고 종료될 때까지 빈의 생명주기를 책임짐
- 싱글톤 패턴: 클래스 자체가 인스턴스를 관리하며 인스턴스 생성과 반환 로직이 클래스 내에 포함되어 있음
주입 방식
- 스프링 싱글톤 스코프: 의존성 주입(Dependency Injection)을 통해 관리됨
- 싱글톤 패턴: 클래스 내부에서 직접 인스턴스를 관리하기 때문에, 의존성 관리가 비교적 제한적
유연성
- 스프링 싱글톤 스코프: 스프링 설정 파일(XML, 자바 어노테이션 등)을 통해 쉽게 설정을 변경할 수 있으며 이를 통해 같은 클래스라도 다른 설정으로 여러 인스턴스를 가질 수 있음
- 싱글톤 패턴: 코드 수정 없이 인스턴스 생성 방식을 변경하기 어려움
참고
코딩으로 학습하는 GoF의 디자인 패턴 - 백기선 강사님
반응형
'Design Pattern' 카테고리의 다른 글
[디자인 패턴] 추상 팩토리 패턴 (Abstract Factory Pattern) (0) | 2024.06.18 |
---|---|
[디자인 패턴] 팩토리 메서드 패턴 (Factory Method Pattern) (0) | 2024.06.18 |
[디자인 패턴] JDK 동적 프록시와 CGLIB (0) | 2021.12.11 |
[디자인 패턴] 프록시 패턴과 데코레이터 패턴 (0) | 2021.12.01 |
[디자인 패턴] 템플릿 메서드 패턴과 콜백 패턴 (0) | 2021.11.25 |