싱글톤 패턴

김하밍·2023년 7월 1일

CS 스터디

목록 보기
1/11

싱글톤 패턴이란 ?

  • 원래 클래스는 여러 개의 인스턴스를 만들 수 있지만, 싱글톤 패턴은 그러지 않고, 하나의 인스턴스를 기반으로 여러 개의 모듈들을 공유하는 형태를 가집니다.
  • 어플리케이션이 시작될 때, 어떤 하나의 클래스가 최초(new) 한 번만 메모리를 할당하고, 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴입니다.
  • 생성자가 여러번 호출되어도 실제로 생성되는 객체는 하나이고, 최초 생성 이후 호출된 생성자는 최초에 생긴 즉, 이미 만들어진 오직 하나의 객체를 반환합니다.


위 그림과 같이, 하나의 클래스를 기반으로 하나의 인스턴스를 만들어놓고 해당 인스턴스를 다른 모듈들이 공유하며 사용합니다.


싱글톤 패턴의 구현 요소 해석하기

class Singleton {
	private static Singleton singleton;	// 1. 정적 참조 변수
	private Singleton() {}	// 2. private 생성자
	public static Singleton getInstance() {	// 3. 객체 반환 정적 메소드
		return singleton;
	}
}
  1. 필드:
    private 을 사용하여 클래스 내부에서만 접근할 수 있도록 합니다. 이로써 외부에서 직접 객체를 생성하거나 접근하는 것을 방지하여 싱글톤 패턴의 유일한 객체 보장을 강화합니다. (유일한 객체 보장)

static 을 객체 생성 없이도 해당 클래스의 정적 메소드를 통해 인스턴스에 접근할 수 있도록 하기 위함 입니다. (클래스 레벨에서의 접근 제어)

  1. 생성자:
    private 을 사용하여 오직 내부에서만 유일한 인스턴스를 생성하도록 보장합니다.
    즉, 인스턴스의 생성과 소멸을 외부에서 조작할 수 없도록 합니다.

  2. 메소드:
    public 을 사용하여 외부에서도 유일한 객체를 공유할 수 있도록 합니다.
    static 을 사용하여 유일한 객체 보장을 위해 객체 생성없이도 호출할 수 있도록 보장합니다. (클래스 레벨에서 호출 가능하기 위해)

결국, getInstance() 메소드는 항상 동일한 인스턴스를 반환합니다.
private static으로 선언된 유일한 객체에 접근하여 이전에 생성된 객체를 반환하므로, 싱글톤 패턴의 유일성을 보장할 수 있습니다.


싱글톤의 장점 및 단점


싱글톤 패턴은 단위 테스트와 같은 TDD(테스트 기반의 개발)를 할 때 문제가 될 수 있습니다. 여러 단위로 쪼갠 단위 테스트가 실행될 때, 테스트끼리 서로 영향을 끼치지 않아야하며 어떤 순서로든 문제없이 실행이 되어야 하는데, 싱글톤 패턴은 하나의 인스턴스에 접근하여 구현하는 방식이므로 각 테스트마다 '독립적'일 수 없습니다.


단점 해결 방법

싱글톤 인스턴스에 의존하는 테스트는 해당 인스턴스의 초기 상태나 다른 테스트에서 변경된 상태에 의해 영향을 받을 수 있습니다. 이로 인해 테스트 결과를 예측하기 어렵게 됩니다.

의존성 주입을 통해 테스트 간에 독립적인 인스턴스를 사용하고 필요한 경우, 테스트용 가짜 객체(Mock Object)를 주입하는 방법이 있습니다. 보통 Java에서는 Mockito 와 같은 Mock Framework를 사용하여 테스트용 가짜 객체를 쉽게 생성하고, 테스트에서 원하는 동작을 정의할 수 있습니다.

이렇게 하면, 테스트 가능성 & 독립성 개선할 수 있으며, 테스트 코드의 유지보수성과 신뢰성을 향상시킵니다.


Java로 싱글톤 패턴을 구현하는 방법 7가지 방법

  1. 단순한 메서드 호출
  2. Synchronized
  3. 정적 멤버
  4. 정적 블록
  5. 정적 멤버와 Lazy Holder(중첩 클래스)
  6. 이중 확인 잠금
  7. enum

멀티 스레드 환경에서 싱글톤 패턴의 중요성

멀티스레드 환경에서 싱글톤 패턴의 사용은 자원의 효율적 관리와 성능 최적화에 필수적입니다.
싱글톤 패턴은 전역에서 단 하나의 인스턴스만을 생성하여 사용하는 디자인 패턴으로, 여러 스레드가 동시에 접근하더라도 오직 하나의 인스턴스만을 공유하게 됩니다.

왜냐하면 멀티스레드 환경에서 각각의 스레드가 객체를 생성할 때마다 리소스를 과도하게 사용하게 되어 성능 저하를 일으킬 수 있기 때문입니다.

이러한 문제를 해결하기 위해 싱글톤 패턴은 초기화 지연(Lazy Initialization)과 함께 사용되곤 합니다. 초기화 지연이란 인스턴스가 실제로 필요할 때까지 생성을 미루는 기법으로, 불필요한 리소스 사용을 줄일 수 있습니다.

하지만 초기화 지연을 멀티스레드 환경에서 사용할 때는 여러 스레드가 동시에 인스턴스 생성 메소드에 접근할 경우 여러 인스턴스가 생성될 위험이 있습니다. 이를 방지하기 위해 동기화(Synchronization) 기법이 필요합니다.

먼저 동기와 비동기 개념에 대해 이해해보겠습니다.

  • 예시: 카페

    동기식 - 한 번에 한 주문씩 처리하는 것
    비동기식 - 요청~결과와 상관없이 다음 주문을 만들기 시작하는 것

  • 비동기식 싱글톤 패턴:
    충돌을 방지하기 위해서 쓰레드 풀이나 비동기 작업 처리를 위한 프레임워크 등을 사용하여 쓰레드의 생성과 관리를 효율적으로 처리할 수 있습니다.

  • synchronized 사용한 동기화:
    한 번에 하나의 스레드만이 동기화된 영역에 진입할 수 있게 되고, 동시에 실행되는 스레드들 사이에서 데이터의 일관성과 안정성을 유지할 수 있다.

public class Singleton {
    private static Singleton instance;

    private Singleton() {} // private constructor to prevent instantiation

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

참고

profile
나만의 언어로 기록하며 성장하기 !

0개의 댓글