이번 포스트는 파이썬으로 작성한점 양해 부탁드립니다.
아래의 포스트를 읽고 오는 것을 권장한다.
https://dev-ryuon.tistory.com/53
필자는 싱글턴 패턴을 사용하면서 한번 생성한 인스턴스를 재사용할 수 있다는 점(메모리 할당 면에서 효율적)과 전역적으로 해당 인스턴스를 사용할 수 있다는 점이 좋았다. 일단 파이썬으로 싱글턴 패턴을 어떻게 구현했는지 확인해보자.
class SingletonInstance:
__instance = None
@classmethod
def __getInstance(cls):
return cls.__instance
@classmethod
def instance(cls, *args, **kwargs):
cls.__instance = cls(*args, **kwargs)
cls.instnace = cls.__getInstance
return cls.__instance
사용법은 간단하다. 위의 싱글턴 패턴을 사용하기 위해서는 싱글턴 패턴을 적용하려는 클래스에 상속을 해주어야 한다. 상속을 받은 클래스는 SingletonInstance 부모 클래스로부터 상속받은 정적 메소드인 instance() 를 호출하면 된다.
그러나 구글링을 해보아도 위 코드에 대한 상세한 설명이 없어서 필자가 해석한 나름대로 끄적여보려 한다.
일단 staticmethod와 classmethod의 차이를 알아야 한다. staticmethod는 자기 자신을 참조할 키워드가 없기 때문에 (self나 cls) 직접 클래스명을 명시하여 멤버를 참조해야 한다. 하지만, classmethod는 cls라는 자기 자신의 클래스를 참조하기 때문에 상속받은 자식 클래스의 멤버들을 참조할 수 있다. (중요한 점은 인스턴스 멤버변수는 참조할 수 없다. self.num 같은거..)
앞서 언급한 instance() 메소드를 호출하면 무슨 일이 일어나는지 한줄한줄 해석해보자.
1. cls(*args, **kwargs)를 호출하게 되면 상속받은 자식 클래스의 생성자를 호출하여 내부적으로 __new__()와 __init__()이 호출되어 초기화된 객체가 자식 클래스의 __instance 클래스 필드에 할당 된다.
2. 그 다음 현재 실행되고 있는 instance 메소드 포인터를 __getInstance 메소드 포인터를 가르키게 된다. 즉, 다음에 instance를 호출할 경우 instance가 아닌 __getInstance가 호출된다는 의미이다.
3. 그리고, 마지막으로 __instance 클래스 필드에 할당 되어 있는 인스턴스를 반환하게 된다.
그리고 다시 instance를 호출하게 된다면?
아까 instance 메소드 내부에서 instance에 대한 메소드 포인터를 __getInstance로 변경했으므로 더 이상 인스턴스를 새로 생성하지 않고 생성되어 있는 인스턴스를 반환하기만 한다.
그러나, 위와 같은 코드의 경우 객체를 없애고 다시 만들고 싶은 경우에는 딱히 좋은 방법이 떠오르지 않았다. 소멸자를 쓰는 것보다 일관적인 방법을 사용하고 싶었다. (메소드를 사용하여) 그래서 필자는 clearInstance와 removeInstance라는 메소드를 만들었다.
class SingletonInstance:
__instance = None
__tmp_method = None
@classmethod
def __getInstance(cls):
return cls.__instance
@classmethod
def instance(cls, *args, **kwargs):
"""
싱글톤 인스턴스는 특정 변수에 할당하지 않는 것이 좋다.
ex)
o1 = Obj.instance()
o2 = Obj.clearInstance() 혹은 Obj.removeInstance()
이후에도 o1 변수가 삭제를 시도한 인스턴스를 참조하고 있어 메모리에 남아있는 모습이 보인다.
import sys
sys.getrefcount(o1) # 0이 아님 (0이 되어야 gc에 의에 메모리에서 해제된다.)
"""
cls.__instance = cls(*args, **kwargs)
cls.__tmp_method = cls.instance # instance 메소드 포인터를 __tem_method 에 백업
cls.instance = cls.__getInstance # instance 메소드 포인터에 __getInstance 메소드 포인터로 대체 (instance 호출시 __getInstance 호출)
return cls.__instance
@classmethod
def removeInstance(cls):
"""
기존에 존재하던 인스턴스를 제거. instance()로 새로 호출해야함.
:return:
"""
if cls.__instance:
cls.__instance = None # 클래스 필드에 할당 되어 있는 인스턴스 제거
cls.instance = cls.__tmp_method # 백업해둔 메소드 포인터를 로드
@classmethod
def clearInstance(cls, *args, **kwargs):
"""
기존에 존재하던 인스턴스를 제거하고 새로운 인스턴스를 반환한다.
:param args:
:param kwargs:
:return:
"""
if cls.__instance:
obj = cls.__new__(cls)
obj.__init__(*args, **kwargs)
cls.__instance = obj
cls.instance = cls.__getInstance
return cls.__instance
clearInstance는 새로운 인스턴스를 만들어서 반환하는 것이고, removeInstance는 기존에 있던 인스턴스를 제거하기만 한다. 다시 instance()를 호출해야 새로운 인스턴스가 생성된다. (초기상태로 되돌리는 효과)
'[CS] - Design Pattern' 카테고리의 다른 글
#4. 스트레티지 패턴 (Strategy Pattern) (0) | 2021.01.19 |
---|---|
#3. 컴포지트 패턴 (Composite Pattern) (0) | 2021.01.19 |
#2. 데코레이터 패턴 (Decorator Pattern) (0) | 2020.12.28 |
#1. 싱글턴 패턴 (Singletone Pattern) (0) | 2020.12.10 |
#0. GoF 디자인 패턴 (2) | 2020.12.07 |
댓글