싱글턴 패턴의 다양한 구현 방식

디자인 패턴 개요

디자인 패턴은 소프트웨어 개발에서 반복적으로 발생하는 문제에 대한 검증된 해결책으로, 코드의 재사용성과 유지보수성을 높이는 데 기여한다. 일반적으로 23가지로 분류되며, 주로 생성형, 구조형, 행동형 세 가지 유형으로 나뉜다.

주요 패턴 카테고리

  • 생성형 패턴 (5종): 팩토리 메서드, 추상 팩토리, 싱글턴, 빌더, 원형
  • 구조형 패턴 (7종): 어댑터, 데코레이터, 프록시, 플라크, 브릿지, 컴포지트, 펄프
  • 행동형 패턴 (11종): 전략, 템플릿 메서드, 옵저버, 이터레이터, 책임 연쇄, 명령, 백업, 상태, 방문자, 중재자, 인터프리터

싱글턴 패턴의 핵심 개념

한 클래스에 대해 시스템 전체에서 단 하나의 인스턴스만 존재하도록 보장하고, 전역적으로 접근 가능한 포인트를 제공하는 디자인 패턴이다. 특히 자원을 많이 사용하거나 초기화 비용이 큰 객체(예: 데이터베이스 연결, 설정 관리)에 적합하다.

다양한 싱글턴 구현 방법

모듈 수준의 싱글턴 (가장 일반적인 방식)

모듈 내부에서 클래스 인스턴스를 미리 생성하고, 외부에서 해당 모듈을 가져와서 사용함으로써 전역 단일 인스턴스를 보장할 수 있다.

# db_module.py
class Database:
    def __init__(self, host):
        self.host = host

    def query(self, sql):
        return f"Executing: {sql} on {self.host}"

db_instance = Database("localhost")

# main.py
from db_module import db_instance
print(db_instance.query("SELECT * FROM users"))

클래스 내부에서 싱글턴 제어

클래스 내부에 `__instance` 속성을 활용하여 인스턴스 생성 여부를 체크하고, 클래스 메서드를 통해 유일한 인스턴스를 반환한다.

class Logger:
    __instance = None

    def __init__(self, level="INFO"):
        self.level = level

    @classmethod
    def get_instance(cls):
        if cls.__instance is None:
            cls.__instance = cls()
        return cls.__instance

# 사용 예
log1 = Logger.get_instance()
log2 = Logger.get_instance()
print(id(log1) == id(log2))  # True

메타클래스를 통한 싱글턴 제어

메타클래스를 정의하여 클래스 생성 시 자동으로 인스턴스를 생성하고, `__call__`을 오버라이드해 인스턴스 생성 로직을 제어한다.

class SingletonMeta(type):
    def __init__(cls, name, bases, attrs):
        cls._instance = None
        super().__init__(name, bases, attrs)

    def __call__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

class ConfigManager(metaclass=SingletonMeta):
    def __init__(self, config_file="app.conf"):
        self.config = config_file

config1 = ConfigManager()
config2 = ConfigManager()
print(id(config1) == id(config2))  # True

데코레이터를 활용한 싱글턴 패턴

데코레이터를 사용하여 클래스 호출 시점에 인스턴스 생성을 제어하며, 인자가 없으면 기존 인스턴스를 반환하고, 인자가 있으면 새로운 인스턴스를 생성한다.

def singleton(cls):
    instance = None
    def wrapper(*args, **kwargs):
        nonlocal instance
        if not args and not kwargs:
            if instance is None:
                instance = cls()
            return instance
        return cls(*args, **kwargs)
    return wrapper

@singleton
class CacheManager:
    def __init__(self):
        self.data = {}

cache1 = CacheManager()
cache2 = CacheManager()
print(id(cache1) == id(cache2))  # True

메타클래스에서 조건부 싱글턴 처리

처음 인자를 받은 경우 그 인스턴스를 싱글턴으로 고정하고, 이후에는 다른 인자를 받더라도 새 인스턴스를 생성하도록 한다.

class ConditionalSingleton(type):
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, '_single'):
            if args or kwargs:
                cls._single = super().__call__(*args, **kwargs)
            else:
                raise RuntimeError("First call must provide arguments")
        else:
            if args or kwargs:
                return super().__call__(*args, **kwargs)
            return cls._single

class ResourceManager(metaclass=ConditionalSingleton):
    def __init__(self, name):
        self.name = name

res1 = ResourceManager("DB")
res2 = ResourceManager("API")  # 새로운 인스턴스
res3 = ResourceManager()       # 싱글턴 반환
print(res1.name, res2.name, res3.name)

태그: 싱글턴 패턴 메타클래스 데코레이터 python 디자인 패턴

7월 26일 11:50에 게시됨