CS/Design Pattern

Singleton Pattern

dawonny 2022. 3. 17. 20:36
728x90
반응형
Singleton Pattern 개요

글로벌하게 접근가능한 단 한개의 객체만을 허용하는 패턴

 

- 클래스에 대한 단일 객체 생성

- 전역 객체 제공

- 공유된 리소스에 대한 동시 접근 제어

- 글로벌 액세스 지점을 제공하는, 단점이 거의 없는 검증된 패턴

생성자를 private로 선언하고, 객체를 초기화하는 static 함수를 만들어 구현할 수 있음.

첫 호출에 객체가 생성되고, 그 후 클래스는 동일한 객체를 계속 반환함.]

class Singleton(object):
  def __new__(cls):
    
    if not hasattr(cls, 'instance'):
      print('create')
      cls.instance = super(Singleton,cls).__new__(cls)
    else:
      print('recycle')
    return cls.instance

s1 = Singleton() #create
print(s1)

s2 = Singleton() #recycle
print(s1)

print(s1 is s2) #true

- 한 개의 Singleton 클래스

  인스턴스를 생성한다

- 이미 생성된 인스턴스가 있다면 재사용한다.

 


Singleton Pattern : Lazy Instantiation

- Singleton 패턴의 한 종류

- 아직 필요하지 않은 시점에 실수로 객체를 미리 생성하는 경우를 방지하기 위함

- Lazy Instantiation은 인스턴스가 꼭 필요할 때 생성하도록 함

- 사용할 수 있는 리소스가 제한적인 상황일 때 객체가 꼭 필요한 시점에 생성

class LazyInstantiation:
  _instance = None
  def __init__(self):
    if not LazyInstantiation._instance:
      print('__init__method called but nothing is created')
    else:
      print('instance already created: ', self.getInstance())
      
  @classmethod
  def getInstance(cls):
    if not cls._instance:
      cls._instance = LazyInstantiation()
    return cls._instance
    
s = LazyInstantiation() #클래스를 초기화 했으나 객체는 생성되지 않음
print(s._instance)
s1 = LazyInstantiation.getInstance() #객체가 생성됨
s2 = LazyInstantiation()


Singleton과 Metaclass

- Metaclass 란?

          클래스를 만드는 클래스

- type은 객체의 클래스 종류를 알아낼 때에도 사용되지만, 클래스를 만들어낼 수도 있음

- type을 상속받게 되면 Meta 클래스가 됨. -> 주로 클래스의 동작을 제어할 때 사용

- 메타클래스는 __call__함수를 통해서 객체 생성에 관한 제어를 할 수 있음.

 

#Metaclass

class MyInt(type):
  def __call__(cls, *args, **kwds):
    print('myint ', args)
    print('Now do whatever you want with these objects...')
    return type.__call__(cls, *args, **kwds)
  
class int(metaclass=MyInt):
  def __init__(self, x, y):
    self.x = x
    self.y = y
    
i = int(4,5)

- *args : 인자를 tuple로 만들어줌

- **kwds : 딕셔너리로 꾸려서 내부로 전달

- __call__ 메소드는 이미 존재하는 클래스의 객체를 생성할 때 호출되는 파이썬의 특수 메소드

- int클래스를 생성하면 MyInt 메타 클래스의 __call__ 메소드가 호출됨

- 객체 생성을 메타클래스가 제어한다는 의미임

 

-> 메타클래스가 클래스와 객체 생성을 제어한다면 싱글톤을 생성하는 용도로 사용할 수 있다는 의미

 

class MetaSingleton(type):
  _instances = {}
  
  def __call__(cls, *args, **kwds):
    if cls not in cls._instances:
      cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwds)
    return cls._instances[cls]
  
class Box(metaclass = MetaSingleton):
  pass

b1 = Box() #생성
b2 = Box() #재사용

print(b1==b2)

Metaclass의 __call__함수 내부에 인스턴스 생성여부를 확인하는 로직이 있으며,

Metaclass에 의해 Singleton으로 지정된 Box 클래스의 인스턴스가 생성될 때

Metaclass의 __call 함수가 호출되면서 Box의 생성을 제어함 (Singleton 성질을 가지게 됨)


Monostate Singleton Pattern

Singleton Pattern의 정의는반드시 한개의 클래스 객체만 존재해야함

하지만, Alex Martelli는 상태를 공유하는 인스턴스가 필요하다고 주장함

-객체의 생성여부보다, 객체의 상태와 행위가 더 중요

 

“Monostate Singleton Pattern은 모든 객체가 같은 상태를 공유하는 패턴"

class MonoState:
  __shared_state = {"1":"2"}
  
  def _init__(self):
    self.x = 1
    self.__dict__ = self.__shared_state
    pass
  
ms1 = MonoState()
ms2 = MonoState()

print(ms1)
print(ms2)

ms2.x = 10

print(ms1.x)
print(ms2.x)

모든 객체는 객체의 변수정보들을 담고 있는 딕셔너리가 존재 

-> __dict__

 

__dict__는 클래스에 속한 변수들을 내부적으로 보관하는 딕셔너리 변수


Singleton Pattern 활용 사례 

DB 활용하는 클라우드 서비스

 

클라우드 서비스 내 DB에 접근하는 여러개의 모듈이 있음

여러 개의 서비스가 한 개의 DB를 공유하고 있는 구조

 

외우기!

 

class MetaSingleton(type):
  _instances = {}
  
  def __call__(cls, *args, **kwds):
    if cls not in cls._instances:
      cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwds)
    return cls._instances[cls]
  
class Database(metaclass = MetaSingleton):
  connection = None
  def connect(self):
    if self.connection is None:
      self.connection = sqlite3.connect("db.sqlite3")
      self.cursorobj = self.connection.cursor()
    return self.cursorobj  
  
db1 = Database().connect()
db2 = Database().connect()

print(db1)
print(db2)

파이썬에서 모든 모듈들은 싱글톤

파이썬의 import 방식 때문에 모든 모듈은 기본적으로 싱글톤임

 

파이썬의 작동방식 살펴보면

- 파이썬 모듈이 import 되었는지 확인

- import 되었다면 해당 개체를 반환하고, 안되었다면 import를 instance화 함

- 모듈은 import와 동시에 초기화되지만, 같은 모듈을 다시 import하면 초기화 X

- 그래서 한개의 객체만 유지하고 반환하는 싱글톤 방식임.

 


Singleton Pattern 의 단점

싱글톤 패턴은 효율적이지만 단점 존재

 

- 전역 변수의 값이 실수로 변경된 것을 모르고 애플리케이션에서 사용될 수 있음

- 같은 객체에 대한 여러 참조자가 생김

- 전역 변수에 종속적인 모든 클래스 간 상호관계가 복잡해질 수 있음.

  전역 변수 수정이 필요하게 되면 의도치 않게 다른 클래스에도 영향을 줄 수 있음.

728x90
반응형