Design Pattern

[Design Pattern] 싱글톤 패턴

JiWonSon 2022. 12. 17. 19:19

1. 싱글톤 패턴?

 클래스 인스턴스를 하나만 만들고, 그 인스턴스로의 전역 접근을 제공하는 디자인 패턴이다.

  • 생성자가 여러번 호출되어도 실제로 생성되는 객체는 하나이며, 최초로 생성된 이후에 호출된 생성자는 이미 생성한 객체를 반환시키도록 만드든 것

 

ex)Singleton 클래스다이어그램

 

  • uniqueInstance라는 클래스 변수에 싱클톤의 하나뿐인 인스턴스가 저장된다.

  • getInstance()메소드는 정적 메소드로 Singleton.getInstance()라는 코드만 사용하면 언제든 이 메소드를 호출할 수 있다. 전역변수에 접근하는 것처럼 '쉬우면서 게으른 인스턴스'를 생성할 수 있다는 장점이 있다.

 

 

 

2. 장점과 단점

1) 장점

  • 객체를 생성할 때마다 메모리 영역을 할당 받아야 한다. 하지만 한번의 new로 객체를 생성한다면, 메모리 낭비를 방지할 수 있다.

  • 싱글톤으로 구현한 인스턴스는 "전역" 이므로 하나뿐인 인스턴스를 어디서든지 접근할 수 있다.

 2) 단점

  • 싱글톤 인스턴스가 혼자 너무 많은 일을 하거나, 많은 데이터를 공유 시키면 다른 클래스 간의 결합도가 높아짐 
    따라서 "느슨한 결합 원칙"을 위배하기 쉬움 ex) 싱글턴을 바꾸면 연결된 모든 객체도 바꿔야 할 가능성이 높음

  • 멀티 스레드 환경에서 동기화 처리를 하지 않았을 때, 인스턴스가 2개 생성되는 문제가 발생할 수 있음 (5번에 해결방안 참조)

3. 사용하는 경우

1) 주로 공통된 객체를 여러개 생성하여 사용해야하는 상황에서 쓴다.

2) 인스턴스가 절대적으로 한 개만 존재한다는 것을 보증하고 싶을 때 

     ex) 스레드풀, 캐시,대화상자, 사용자 설정을 처리하는 객체, 로그 기록용 객체 등

 

4. 전역변수 대신 싱글톤 패턴 사용 이유

 객체를 하나 생성하여 공통으로 사용하는 것이 목적이라면, 간편하게 전역변수를 사용해도 되지 않나는 반문을 할 수 있다. 하지만, 전역변수를 생성한다면 다음과 같은 문제점이 있다.

1) 애플리케이션이 끝날 때까지 객체를 한 번도 쓰지 않는다면 자원만 잡아먹는 쓸데없는 객체가 된다.

2) 게으른 인스턴스를 생성할 수 없다. 

3) 간단한 객체의 전역 레퍼런스를 자꾸 만들게 되어 네임스페이스 역시 지저분하게 만들어진다.



5. 멀티스레딩 문제 해결방법

1) Lazy Initialzation 게으른 초기화

public class Singleton{
    private static Singleton uniqueInstance;
    private Singleton() {}
    public static synchronized Singleton getInstance() {
    	if (uniuqeInstance == null) {
        	uniqueInstance = new singleton();
        }
        return uniqueInstance;
    }
    
}
  • private static으로 인스턴스 변수 생성

  • private으로 생성자 만들어 외부에서의 생성 막기

  • 메소드를 동기화하는 것이 성능이 100배정도 저하될 수 있다는 큰 단점이 있어 권장하지는 않음



2) 인스턴스가 필요할 때는 생성하지말고 처음부터 만들기

public class Singleton{
    //정적 초기화 부분에서 인스턴스 생성하기
    private static Singleton uniqueInstance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
    	return uniqueInstance;
    }
}
  • 클래스가 로딩될 때 Singleton의 하나뿐인 인스턴스를 생성해준다. 

  • 인스턴스 생성 전까지는 어떤 스레드도 uniqueInstance 정적 변수에 접근할 수 없다.

  • 가장 많이 사용되는 기법 



3) DCL(Double-Checked Locking)을 사용하여 동기화부분 줄이기

public class Singleton{
    private volatile static Singleton uniqueInstance;
    private Singleton() {}
    public static Signleton getInstance() {
    	if (uniqueInstance == null){
        	synchronized (Singleton.class) {
            		if (uniqueInstance == null) {
                		uniqueInstance = new Singleton();
                	}
            }
       }
       return uniuqeInstance;    
    }
}
  • 인스턴스가 생성되어 있는지 확인한 다음 생성되지 않을 때만 동기화할 수 있다.

  • 즉, 처음에만 동기화하고 나중에는 동기화하지 않아도 됨

  • 이 방법을 사용하지 않는 이유는 개발자가 직접 동기화 문제에 대한 코드를 작성하면, 프로그램 구조가 복잡해지고 정확성이 떨어짐 
    따라서 2번과 같이 JVM 클래스 초기화 과정에서 싱글톤 초기화 문제에 대한 책임을 JVM에 넘기는 것이 더 정확도 측면에서 좋음


'Design Pattern' 카테고리의 다른 글

[Design Pattern] 템플릿 메소드 패턴  (0) 2023.03.05
[Design Pttern] 데코레이터 패턴  (2) 2023.02.05
[Design Pattern] MVC패턴  (0) 2023.01.09
[Design Patter] 상태패턴  (0) 2023.01.07