2023. 11. 21. 02:00ㆍDesignPattern
싱글톤 패턴이란, 특정 클래스가 프로그램 전체에서 오직 하나만 생성되도록 보장하는 패턴입니다.
또한 전역적으로 하나의 인스턴스를 유지하며, 리소스의 중복사용을 방지하고, 전역적인 접근 지점을 제공합니다.
싱글톤패턴을 사용하기 위해선 몇 가지 사항을 고려해야 합니다.
- 스레드 안전성: 멀티스레드 환경에서 싱글톤 인스턴스의 생성과 접근은 스레드 안전하게 관리되어야 합니다.
- 예외 처리: 초기화 과정에서 발생할 수 있는 예외 사항들을 적절히 처리해야 합니다.
- 자원 관리: 싱글톤으로 생성된 인스턴스가 사용하는 자원(파일 핸들, 네트워크 연결 등)은 적절히 관리하고 해제해야 합니다.
프로그램을 작성하다 보면 하나만 가지고 있어도 괜찮은 요소들이 있어서 싱글톤 패턴을 활용하여 리소스를 크게 줄일 수 있습니다.
아래 예시코드는 사이드 프로젝트 코드의 일부이고 싱글톤패턴을 활용하고 공부해 보게 되는 고민으로 이어진 코드입니다.
- 기존 로스트아크 OPEN API 통신연결을 위한 클래스
class LoaGateWay:
def __init__(self) -> None:
load_dotenv()
self.base_url = "https://developer-lostark.game.onstove.com/"
self.session = requests.Session()
headers = {
"accept": "application/json",
"authorization": f"bearer {os.environ.get('LOA-API-KEY')}",
}
self.session.headers.update(headers)
위 코드에서 생성되는 요소들은 여러 번 생성될 필요가 없는 요소이고 변화가 없다시피 하는 요소입니다.
따라서 연결에 필요한 공통요소와 세션을 부모 클래스로 생성해 주고,
각 기능별 API를 호출하고 처리하는 로직이 들어가는 자식 클래스들을 만들어주는 설계를 생각했습니다.
- 싱글톤 패턴을 적용하여 리팩터링 한 LoaGateWay 클래스
class LoaGateWay:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(LoaGateWay, cls).__new__(cls)
cls._instance.__init__(*args, **kwargs)
return cls._instance
def __init__(self) -> None:
if not hasattr(self, "_initialized"):
load_dotenv()
self.base_url = "https://developer-lostark.game.onstove.com/"
self.session = requests.Session()
headers = {
"accept": "application/json",
"authorization": f"bearer {os.environ.get('LOA-API-KEY')}",
}
self.session.headers.update(headers)
self._initialized = True
위 코드를 보면 __new__()에서
클래스를 호출하여 인스턴스를 만들 때,_instance
가 있는지 없는지를 판단하여 없으면 해당 인스턴스를 만들어주고 [cls._instance.__init__(*args, **kwargs)
], 있다면 기존에 생성된 인스턴스를 리턴해줍니다.
또한 이미 호출되어 인스턴스가 생성된 상태에서 추가적으로 인스턴스를 호출할 때 초기화__init__()에서_initialized
True/False값을 통하여 초기화의 중복을 방지합니다.
디자인 패턴에 대한 첫 게시물이고 , 처음으로 접한 패턴이라 마냥 좋은 줄만 알았는데
대부분 싱글톤패턴을 사용하면 10중 9는 프로그램 디자인 기본원칙을 위배한다고 한다.
그래서 싱글톤 패턴의 대안 패턴이 어떤 것이 있는지 간단하게 알아보고 , 추후에 다뤄볼 기회가 있다면 다뤄볼 생각이다.
- 서비스 로케이터 패턴
서비스와 의존성을 중앙 레지스트리에 등록하고, 필요할 때 이를 조회하여 사용합니다. 이는 의존성 관리를 유연하게 만듭니다.
- 의존성 주입 패턴
객체의 의존성을 외부에서 주입함으로써, 객체 간의 결합도를 낮추고 유연성 및 테스트 용이성을 증가시킵니다.
- 팩토리 메서드 패턴
객체 생성을 서브클래스에 위임하여, 클라이언트 코드가 특정 클래스에 직접 의존하지 않도록 합니다. 이를 통해 생성 과정의 캡슐화와 유연성을 제공합니다.
- 레지스트리 패턴
전역 레지스트리를 통해 객체 인스턴스를 관리하고, 필요할 때 해당 객체에 접근할 수 있도록 합니다. 이는 여러 객체 인스턴스의 관리를 용이하게 합니다.