싱글톤 패턴의 두 가지 유형

개발 끄적끄적 .. ✍️·2021년 12월 21일
0

싱글톤 디자인 패턴

  • 싱글톤 디자인 패턴은 글로벌하게 접근 가능한 하나의 객체를 제공하는 패턴
  • 동일한 리소스에 대한 동시 요청의 충돌을 방지하기 위해 하나의 인스턴스를 공유하는 작업에 주로 사용
  • 싱글톤 패턴은 객체 생성의 관점과 객체의 상태의 관점에서 방식의 차이가 존재

게으른 초기화(객체 생성의 관점)

  • 싱글톤 패턴을 기반으로 하는 초기화 방식
  • 모듈을 임포트 할 때 아직 필요하지 않은 시점에 실수로 객체를 미리 생성하는 경우가 있음
  • 게으른 초기화 방식은 인스턴스를 꼭 필요할 때 생성
  • 게으른 초기화 기본 코드
class Singleton(object):
  __instance = None
  def __init__(self):
    if not Singleton.__instance:
      print("__init__ method called")
    else:
      print("Instance already created:", self.getInstance())
  
  @classmethod
  def getInstance(cls):
    if not cls.__instance:
      cls.__instance = Singleton()
    return cls.__instance

s = Singleton()
print("Object created", Singleton.getInstance())
s1 = Singleton()

모노스테이트 싱글톤 패턴

  • 객체의 생성 여부보다는 객체의 상태와 행위가 더 중요하다고 여기는 싱글톤 기반 디자인 패턴
  • 이름 그대로 모든 객체가 같은 상태를 공유하는 패턴
  • 모노스테이트 싱글톤 패턴의 기본 코드
class Borg(object):
  __shared_state = {"1": "2"}
  def __init__(self):
    self.x = 1
    self.__dict__ = self.__shared_state
    pass
  
b = Borg()
b1 = Borg()
b.x = 4

print("Borg Object 'b': ", b)
print("Borg Object 'b1': ", b1)
print("Borg Object 'b': ", b.__dict__)
print("Borg Object 'b': ", b1.__dict__)

내가 경험한 싱글톤 패턴

아주 운이 좋게도 위에서 언급한 두 가지 싱글톤 디자인 패턴을 회사 코드에서 경험 할 수 있었다.

  • 게으른 초기화 싱글톤 패턴
    • 이는 불필요한 메모리 사용을 줄이기 위해 채택된 방식으로 많은 개발자들이 사용하고 있다.
    • 실제 다양한 프로젝트에서 http 객체, db 객체, logging객체 등 빈번하게 호출 되는 객체를 대상으로 싱글톤 패턴이 사용되고 있었다.
    • 프로그램이 실행되고 해당 객체가 처음 호출된 이후 요청이 있을 때마다 같은 객체가 return 되면서 불필요한 리소스 낭비를 줄이고 있었다.
    • 예전에 팀장님께서 내 코드를 보시더니 "이러면 객체가 계속 생성됩니다 싱글톤 패턴에 대해 공부해보세요"라고 말씀해주신적이 있는데 그때는 생성되면 뭐 어때..? 라고 생각해었는데 이제서야 조금은 이해가 되었다ㅎ..
  • 모노스테이트 싱글톤 패턴
    • 멀티프로세싱 프로그램을 통해 모노스테이트 싱글톤 패턴을 처음 보게 되었다.
    • 실제 프로세스 전체가 sharedData라는 하나의 객체를 공유하면서 모두가 같은 상태를 공유할 수 있었다.
    • 실제 파이썬에는 멀티프로세싱 공유 객체를 위한 multiprocessing.Manager라는 라이브러리가 있다.
    • 여기서 중요한 점은 전체의 프로세스가 같은 객체의 상태를 공유해야한다. 그렇기 때문에 예를 들어, 1번 프로세스가 공유 자원에 접근 하여 어떠한 작업을 진행 할 때는 다른 프로세스는 잠시 대기를 해야한다.
    • 이러한 이슈를 mutex(뮤텍스)와 semaphore(세마포어)라고 한다.
      • 뮤텍스: 공유 리소스에 한 번에 하나의 스레드만 접근 할 수 있도록 하는 상호 배제 동시성 제어 정책
      • 세마포어: 1보다 큰 수로 시작. 한번에 자원에 접근할 수 있는 스레드의 수
    • 파이썬에 multiprocessing 패키지에는 위의 문제들을 예방하고자 Lock과 Semarphore 모듈이 있다.
    • 따라서 전체의 공유 자원에 접근 할 때는 아래와 같이 시도한다. 예를 들어 공유 자원에 대한 어떠한 post 작업이 있을 때
      def post(self, category, key, value):
          self._lock.acquire()
          if not self._sharedData.get(category):
              self._sharedData[category] = {}
          temp = self._sharedData.get(category)
          temp.update({key: value})
          self._sharedData[category] = temp
          self._lock.release()
      • 접근을 시도하는 시점에 acquire() 메소드를 사용하여 다른 프로세스의 접근을 막고, 작업이 종료된 이후 release() 매소드를 통해 다른 프로세스의 접근을 허용한다.

마무리

  • 왜 많은 사람들이 싱글톤 디자인 패턴을 프로그래밍에 채택하고 사용하는지 알게 되었다.
  • 객체의 생성의 관점과 컴퓨팅 리소스 관점에서 보았을 때는 컴퓨팅 리소스는 한정적이고 그것을 최대한 활용하기 위한 아주 효율적인 방법이다.
  • 또한 객체의 상태의 관점에서 보았을 때 모두가 하나의 객체 상태를 공유하게 되면서 멀티 프로세스 프로그램에서 다양한 방법으로 활용할 수 있다. (단 모두가 전체의 공유자원에 접근할 수 있다는 것은 또 다른 이슈가 발생할 여지가 있기 때문에 항상 더 신중해야한다)

0개의 댓글