싱글턴 패턴은 어떤 클래스에 대해 오직 하나의 인스턴스만 생성하도록 하는 디자인 패턴이다.
이 패턴을 사용하는 주된 이유 중 하나는 전역 상태를 관리하거나 리소스를 효율적으로 사용하기 위함이다.
왜 정적 변수가 아닌 싱글턴을 써야 하는가?
게으른 로딩(Lazy Loading)
정적 변수는 클래스가 로딩되는 시점에서 즉시 초기화된다.
하지만 게으른 로딩을 통해 인스턴스가 필요한 시점까지 생성을 미룰 수 있는 싱글턴 패턴은 자원을 효율적으로 활용할 수 있다.
특히, 애플리케이션이 시작될 때 필요하지 않은 경우에는 초기화를 지연시키는 것이 좋다.
동적 로딩(Dynamic Loading)
정적 변수는 클래스가 로딩되는 시점에서 생성되기 때문에 동적인 상황에서의 활용이 어렵다.
싱글턴 패턴을 사용하면 객체 생성 로직을 변경하지 않고도 필요한 시점에서 인스턴스를 생성할 수 있다.
유연성과 확장성
싱글턴 패턴은 인스턴스 생성에 대한 제어를 효과적으로 관리할 수 있어서 나중에 상황이 변하거나 확장이 필요한 경우,
싱글턴 클래스의 내부 로직을 변경하지 않고도 대처할 수 있다.
테스트 용이성
정적 변수는 일반적으로 상태를 가지며, 이는 테스트에서 문제를 발생시킬 수 있다.
반면 싱글턴 패턴은 인터페이스를 통한 의존성 주입을 사용하기 용이하다.
주의할 점
멀티스레드 환경에서의 문제
정적 변수로 싱글톤을 구현하면 여러 스레드가 동시에 해당 클래스의 인스턴스를 생성하려고 할 수 있다.
이로 인해 둘 이상의 인스턴스가 생성될 가능성이 있다.
Double-Checked Locking 문제
정적 변수에서 멀티스레드 환경을 고려하여 인스턴스를 생성하기 위해 "Double-Checked Locking"을 사용하는 경우,
초기화된 인스턴스를 반환하지만, CPU 명령어의 재배치 등으로 인해 다른 스레드에서 올바르게 초기화되지 않은 인스턴스를 얻을 수 있다.
Synchronized 사용의 성능 문제
멀티스레드 환경에서 인스턴스 생성을 안전하게 보장하기 위해 synchronized 키워드를 사용하면 성능 문제가 발생할 수 있다.
Synchronized는 동기화된 블록으로 인해 다수의 스레드가 기다리게 되어 성능 저하를 일으킬 수 있다.
이러한 이유로 enum을 사용하는 방법이 있다.
enum을 사용하면 동기화 문제, 클래스 로딩 문제, 리플렉션, 직렬화와 역직렬화 문제등을 쉽게 해결할 수 있다.
// 일반적인 싱글톤 패턴을 구현한 클래스
public class SingleToneObject {
// volatile 키워드를 사용하면 해당 변수에 대한 모든 쓰기 연산이 메인 메모리에 즉시 반영되어,
// 다른 스레드에서 읽을 때 항상 최신 값을 얻을 수 있다.
private volatile static SingleToneObject instance;
// private 생성자를 통해 외부에서의 인스턴스 생성을 방지한다.
private SingleToneObject() {}
// 예제 메서드: 싱글톤이 수행할 동작을 정의합니다.
public void doSomething() {
// 동작 정의
}
// 싱글톤 인스턴스를 반환하는 메서드
public static SingleToneObject getInstance() {
// 첫 번째 null 체크: 인스턴스가 초기화되지 않은 경우에만 동기화 블록으로 진입한다.
if (instance == null) {
// 동기화 블록: 멀티스레드 환경에서 인스턴스가 한 번만 생성되도록 보장한다.
synchronized (SingleToneObject.class) {
// 두 번째 null 체크: 인스턴스가 다른 스레드에 의해 초기화되지 않았는지 다시 확인한다.
if (instance == null) instance = new SingleToneObject();
}
}
return instance;
}
}
// ENUM을 사용하여 싱글톤 패턴을 구현한 클래스
public enum SingletonEnum {
INSTANCE; // 단일 인스턴스를 나타내는 enum 상수
// 싱글톤 인스턴스에서 사용할 다른 멤버 변수나 메서드를 추가할 수 있다.
// 예제 메서드: 싱글톤이 수행할 동작을 정의한다.
public void doSomething() {
// 동작 정의
}
}
'디자인 패턴' 카테고리의 다른 글
template method pattern (0) | 2024.07.05 |
---|---|
State Pattern (0) | 2024.06.15 |
Proxy Pattern (0) | 2024.06.15 |
Observer Pattern (0) | 2024.05.30 |
Mediator Pattern (0) | 2024.05.28 |