개발/디자인패턴

[Java][디자인 패턴] 2. 싱글턴 패턴 (Singleton Pattern)

nova_dev 2022. 3. 1. 23:59
반응형

디자인 패턴

[Java][디자인 패턴] 2. 싱글턴 패턴 (Singleton Pattern)

싱글턴은 생성 패턴으로, 다른 생성 패턴과 달리 클래스에서 하나의 객체만 생성할 수 있도록 제한하는 패턴으로, 생성된 객체는 공유되어 어디서든 접근할 수 있다.

 

 싱글턴 패턴은 언제 사용할까?

  1. 공유 자원 접근
  2. 복수의 시스템이 하나의 자원에 접근할 때
  3. 유일한 객체가 필요할 때
  4. 값의 캐시가 필요할 때

 

 싱글턴 패턴에서는 어떻게 객체를 하나로 보증할까?

  • 생성자의 접근 속성을 private으로 하여 외부에서 생성자에 접근할 수 없도록 한다.
  • 내부 참조체를 통해 자신의 객체를 보관하고, new 키워드 대신 객체 생성 메서드를 호출하여 객체를 가져온다.
  • 이때, getInstance 메서드는 정적 메서드로 만들고 객체가 없을 경우에는 새로운 객체를 생성하고 객체가 존재할 경우에는 기존에 생성된 참조체를 반환한다. (이런 구조는 플라이웨이트 패턴 방식도 응용한 것이다.)

 

 싱글턴 패턴 예시 코드

public class NormalConfig {
}
public class SingletonConfig {
    private static SingletonConfig singletonConfig = null;

    private SingletonConfig() {
    }

    public static synchronized SingletonConfig getInstance() {
        if (singletonConfig == null) {
            singletonConfig = new SingletonConfig();
        }
        return singletonConfig;
    }
}
class SingletonConfigTest {

    @DisplayName("싱글턴으로 생성한 Config 객체의 주소가 일치한다.")
    @Test
    void singletonTest() {
        // given
        SingletonConfig singletonConfig1 = SingletonConfig.getInstance();
        SingletonConfig singletonConfig2 = SingletonConfig.getInstance();

        // when

        // then
        Assertions.assertEquals(singletonConfig1, singletonConfig2);
    }

    @DisplayName("일반 객체로 생성한 Config 객체의 주소가 일치하지 않는다.")
    @Test
    void normalObjectTest() {
        // given
        NormalConfig config1 = new NormalConfig();
        NormalConfig config2 = new NormalConfig();

        // when

        // then
        Assertions.assertNotEquals(config1, config2);
    }
}

 

 정적 클래스와 싱글턴의 차이점

  • 싱글턴 패턴
    • 싱글턴은 메모리 자원에 할당하여 동적 객체를 만든다. (메모리 상주)
    • 선언된 정적 메서드를 통해 자체 객체를 생성하며, 생성된 객체는 정적 프로퍼티에 저장한다.
    • 싱글턴 패턴은 상속받을 수 있다. 생성자를 private이 아니라 protected로 하여 클래스를 상속하고 확장할 수 있다.
  • 정적 클래스
    • 정적 클래스는 코드가 실행되면서 고정적으로 바인딩한다. (비동적)
    • 정적 클래스는 다형성을 위한 인터페이스를 사용할 수 없다.
    • 필요한 모든 동작 기능이 정적 클래스 안에 존재해야 한다.

 

 싱글턴 패턴의 단점 (주의할 점)

1. 경합 조건

  • 싱글턴은 하나의 객체만 생성하는 패턴이지만 특수한 환경에서 단일 객체 생성을 보장하지 못하는 경우도 있다.
  • 멀티 스레드 환경에서 싱글턴 패턴을 사용할 때는 객체 생성이 동시에 요청되는 경우 경합성이 발생할 수 있다.
  • 경합 조건은 동일한 메모리나 자원에 동시에 접근하는 것. 2개 이상의 스레드가 동일한 자원을 사용할 경우 충돌이 발생한다.
    ( 경합 조건 문제 해결 방법 )
    1. 정적 호출을 통해 생성 호출 전까지는 객체를 만들지 않는다. (늦은 초기화)
    2. getInstance와 같이 객체를 생성하고 가져오는 코드에 syncronized 키워드를 붙인다.
  • 경합성과 늦은 초기화 문제를 보완하기 위해 시스템 부팅 시 필요한 싱글턴 객체를 미리 생성. 미리 공유 자원을 만듦으로써 프로그램 실행 도중 발생할 경합성 충돌을 최소화할 수 있음.
    ex) 스프링 컨테이너가 빈 객체를 미리 등록해 두는 것

2. 메모리 문제

  • 하지만 불필요한 객체를 모두 생성하여 메모리에 상주시키는 것은 메모리 낭비일 수 있다.
  • 싱글턴 패턴으로 생성한 자원은 프로그램이 종료될 때까지 메모리에 상주할 수 있다.

 

 Github 코드

Github 예제 코드 링크

Github 예제 테스트 코드 링크

관련 글

 
 

 

 

반응형