정의
- 싱글톤 패턴은, 하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴이다. 보통 데이터베이스 모듈에 많이 사용한다.
- 하나의 인스턴스를 만들어 놓고, 해당 인스턴스를 다른 모듈들이 공유하며 사용하기 때문에, 인스턴스를 생성할 때 드는 비용이 줄어드는 장점이 있다.
자바에서의 싱글톤 패턴
자바로는 다음과 같이 중첩 클래스를 이용해서 만드는 것이 가장 대중적인 방법이다.
class Singleton {
private static class singleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return singleInstanceHolder.INSTANCE;
}
}
public class HelloWorld {
public static void main(String[] args) {
Singleton a = Singleton.getIntance();
Singleton b = Singleton.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
if(a == b) {
System.out.println(true);
}
}
}
/*
705927765
705927765
true
*/
자바로 싱글톤 패턴을 구현하는 6가지 방법
1. 단순한 메서드 호출
- 문제점: 원자성이 결여되어있다 => 자바에서는 멀티 스레드로 동작하므로, 문제가 발생할 수 있다. 즉, 여러 스레드에서 동시에 인스턴스를 생성해서 여러개의 인스턴스가 생길 수 있다는 문제가 있다.
- 해결방법: syncronized 키워드를 메서드 앞에 붙혀서 해당 문제점을 해결할 수 있다. 하지만 락이 걸려있으므로, 성능 저하 이슈가 있을 수 있다.
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
2. 정적 멤버
- 정적(static) 멤버나 블록은 런타임이 아니라, 최초에 JVM이 클래스 로딩 때 모든 클래스를 로드할 때 미리 인스턴스를 생성하는데, 이를 이용한 방법이다.
- 클래스 로딩과 동시에 싱글톤 인스턴스를 만들고, 그렇기 때문에 모듈들이 싱글톤 인스턴스를 요청할 때 그냥 만들어진 인스턴스를 반환하면 된다.
- 문제점: 불필요한 자원 낭비라는 이슈가 있다. 싱글톤 인스턴스가 필요없을 경우에도, 무조건 싱글톤 클래스를 호출해 인스턴스를 만들어야 하기 때문이다.
public class Singleton {
private final static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
3. 정적 블록
public class Singleton {
private static Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
4. 정적 멤버와 LazyHolder (중첩 클래스)
- singleInstanceHolder라는 내부 클래스를 하나 더 만듬으로써, Singleton 클래스가 최초로 로딩되더라도 함께 초기화가 되지 않고, getInstace()가 호출될 때, singleInstanceHolder 클래스가 로딩되어 인스턴스를 생성하게 된다.
class Singleton {
private static class singleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return singleInstanceHolder.INSTANCE;
}
}
5. 이중 확인 잠금(DCL)
- 이중 확인 잠금은, 인스턴스 생성 여부를 싱글톤 패턴 잠금 전에 한번, 객체를 생성하기 전에 한번 2번 체크하면 인스턴스가 존재하지 않을때만 잠금을 걸 수 있기 때문에 앞서 생겼던 문제점들을 해결할 수 있다.
public class Singleton {
private volatile Singleton instance;
private Singleton() {
}
public Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
}
}
6. enum
- enum 클래스는 기본적으로 thread safe한 점 보장되기 때문에, 이를 통해 생성할 수 있다.
public enum SingletonEnum {
INSTANCE;
public void oortCloud() {
}
}
위 6가지 방법에서 추천하는 방법은 제일 많이 쓰이는 4번(정적 멤버와 LazyHolder)와 6번이다.
6번은 이펙티브 자바를 쓴 조슈아 브로크가 추천한 방법이다.
싱글톤 패턴의 단점
- 싱글톤 패턴은 TDD할 때 걸림돌이 된다.
- TDD를 할 때, 단위 테스트는 테스트가 서로 독립적이어야 하며, 테스트를 어떤 순서로든 실행할 수 있어야한다.
- 하지만, 싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이므로, 각 테스트마다 독립적인 인스턴스를 만들기가 어렵다.
- 의존성 주입
- 싱글톤 패턴은 모듈 간의 결합을 강하게 만들 수 있다는 단점이 존재한다. 이를 의존성 주입을 통해 모듈간의 결합을 조금 더 느슨하게 만들어 해결할 수 있다.
- 메인 모듈이 직접 다른 하위 모듈에 대한 의존성을 주기 보다는, 중간에 의존성 주입자가 이 부분을 가로채 메인 모듈이 간접적으로 의존성을 주입하는 방식이다.
- 의존성 주입을 할때는, 상위 모듈은 하위 모듈서 어떠한 것도 가져오지 않아야한다. 또한, 둘 다 추상화에 의존해야 하며, 이때 추상화는 세부사항에 의존하지 말아야한다. 라는 의존성 주입 원칙을 지켜주면서 만들어야 한다.
참고자료
면접을 위한 CS 전공지식 노트
https://www.youtube.com/watch?v=4Sk9dzXgKwo
'Computer Science > Design Pattern' 카테고리의 다른 글
[디자인 패턴] 옵저버(Observer) 패턴 (0) | 2023.03.18 |
---|---|
[디자인 패턴] 전략(Strategy) 패턴 (0) | 2023.03.15 |
[디자인 패턴] Factory 패턴 (0) | 2023.03.14 |