semtax의 개발 일지

디자인패턴 공부내용 정리 : 싱글톤 패턴 본문

개발/Java

디자인패턴 공부내용 정리 : 싱글톤 패턴

semtax 2020. 5. 16. 22:37
반응형

개요

이번에는, 생성 패턴 중 하나인 싱글톤 패턴에 대해서 알아보도록 하겠습니다.

왜 나오게 됬는가?

싱글톤 패턴은, 여러 객체에서 데이터를 공유해서 써야하는데 단 1개만 생성해서 써야할때 어떻게 해야할까 라는 고민에서 나오게 된 패턴입니다. (OOP의 전역변수 라는 느낌으로 생각하시면 됩니다.)

주로, 여러곳에서 쓰이는 설정 정보나, 다른곳에서도 공통적으로 쓰이는 데이터베이스 설정 객체와 같은 정보들을 공유할 때 사용되게 됩니다.

문제 상황

먼저 아래의 상황을 한번 가정 해봅시다.

당신이 앱을 개발한다고 가정을 해봅시다.

그런데, 앱에 있는 여러 클래스에서 앱에서 전역으로 쓰이는 설정정보를 읽고 써야하는 상황이 닥쳤습니다.

이럴때 당신은 어떻게 하겠습니까?

위와 같은 상황에 처했을때 과연 어떻게 해결하면 되는것일까요?

해결법

모든 클래스에서 접근 가능한 전역객체를 만들면 됩니다. 또한 객체를 생성할때, 딱 1개만 생성되게 강제를 시키면 됩니다.

구현

일단 싱글톤 패턴은 아래와 같이 간단하게 구현이 가능합니다.

public class ConfigContext {

  private static ConfigContext configInstance;

  private ConfigContext() {

  }


  public static synchronized ConfigContext getInstance() {
    if(configInstance == null) {
      configInstance = new ConfigContext();
    }
    return configInstance;
  }
}

먼저, 다른 객체에서 객체 생성을 할 수 없게, private 로 생성자를 만들어 줍니다.

그리고 나서, 실제로 생성된 객체를 반환하는 getInstance() 함수를 구현합니다. 이때, 객체가 생성되지 않은 경우 객체를 생성해서 반환을 해주고 이미 객체가 생성된 경우에는 만들어진 객체를 반환하게 구현합니다.

여기서, synchronized 라는 키워드가 보이는데, 이는 다른 여러 스레드에서 해당 객체에 동시접근하는 경우 Race condition이 발생할 수 있기 때문에 synchronized 를 이용해서 동기화를 해주게 됩니다.

예시

일단 싱글톤을 사용하는 예시 코드로 안드로이드(AOSP)의 Choreographer 라는 클래스를 예로 들도록 하겠습니다.

Choreographer 클래스는 실제 모바일 화면의 입출력과 관련된 클래스로 하드웨어의 vsync 신호(화면 수직동기화 신호)를 받고 그에 따른 동작을 세팅할 수 있는 콜백 함수들이 있는 클래스 입니다.

(실제 코드는 https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/view/Choreographer.java 여기에 있습니다.)

주로, FPS(1초에 몇 프레임이 나오는지)와 같은 정보들을 측정할때 사용하는 클래스입니다.

안드로이드 에서는 해당 클래스를 통해서만 vsync 신호가 전송되야 되므로, 단 1개의 객체만 생성이 되야 합니다. 따라서, 싱글톤 패턴을 사용하기 적합하다 할 수 있겠습니다.

실제로 Choreographer 클래스는 아래와 같이 싱글톤 객체를 생성합니다.

public final class Choreographer {
    private static final String TAG = "Choreographer";

    ...

      private static final ThreadLocal<Choreographer> sSfThreadInstance =
            new ThreadLocal<Choreographer>() {
                @Override
                protected Choreographer initialValue() {
                    Looper looper = Looper.myLooper();
                    if (looper == null) {
                        throw new IllegalStateException("The current thread must have a looper!");
                    }
                    return new Choreographer(looper, VSYNC_SOURCE_SURFACE_FLINGER);
                }
            };


      .....

      private Choreographer(Looper looper, int vsyncSource) {
          mLooper = looper;
          mHandler = new FrameHandler(looper);
          mDisplayEventReceiver = USE_VSYNC
            ? new FrameDisplayEventReceiver(looper, vsyncSource)
            : null;
          mLastFrameTimeNanos = Long.MIN_VALUE;

          mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

          mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
          for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
          }
          // b/68769804: For low FPS experiments.
          setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
     }
}

또한, 스프링에서 Bean 으로 등록된 객체들도, 기본적으로 싱글톤 객체로 생성이 됩니다.

출처

  1. https://refactoring.guru/design-patterns/singleton
  2. https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/view/Choreographer.java
반응형
Comments