본문 바로가기

개발

파이썬에서 자주 쓰이는 디자인 패턴 5가지 🐍

반응형

1. 싱글턴 패턴 (Singleton Pattern)

  • 개념: 클래스의 인스턴스가 오직 하나만 생성되는 것을 보장하는 패턴입니다. 이 인스턴스는 프로그램 어디서든 접근할 수 있습니다.
  • 파이썬에서의 활용: 데이터베이스 연결, 로깅 객체, 설정 관리와 같이 전체 애플리케이션에서 공유되어야 하는 자원에 주로 사용됩니다. 파이썬에서는 모듈(module) 자체가 싱글턴의 역할을 합니다. 모듈은 처음 한 번만 로드되므로, 모듈 변수나 함수를 통해 상태를 공유하면 자연스럽게 싱글턴 패턴을 구현할 수 있습니다.
  • 구현 예시:다른 파일에서 import logger를 하면, Logger 클래스의 인스턴스는 오직 한 번만 생성됩니다. 이는 클래스 수준에서 복잡한 로직을 추가하는 대신 파이썬의 모듈 시스템을 활용하는 파이썬다운(Pythonic) 방식입니다.
    # logger.py
    class Logger:
        def __init__(self):
            print("Logger instance created.")
    
    logger = Logger()
    

2. 팩토리 패턴 (Factory Pattern)

  • 개념: 객체를 생성하는 데 필요한 로직을 별도의 클래스나 메서드에 캡슐화하는 패턴입니다. 클라이언트는 특정 클래스를 직접 호출하는 대신, 팩토리(Factory)를 통해 객체를 생성합니다.
  • 파이썬에서의 활용: 다양한 유형의 객체를 생성해야 하지만, 클라이언트가 그 생성 과정을 몰라도 될 때 유용합니다. 예를 들어, 웹 프레임워크에서 여러 종류의 데이터베이스 커넥션을 생성하거나, 다양한 파일 형식(JSON, XML 등)의 파서를 생성하는 경우에 사용됩니다.
  • 구현 예시:이 방식은 객체 생성 코드를 한 곳에 모아 관리하기 쉽게 하고, 새로운 객체 타입이 추가되어도 팩토리 함수만 수정하면 됩니다.
    class ProductA: ...
    class ProductB: ...
    
    def get_product(product_type):
        if product_type == 'A':
            return ProductA()
        elif product_type == 'B':
            return ProductB()
        else:
            raise ValueError("Invalid product type.")
    
    product = get_product('A')
    

3. 옵저버 패턴 (Observer Pattern)

  • 개념: 한 객체(주제, Subject)의 상태 변화를 관찰자(Observer)들이 자동으로 통보받는 일대다(one-to-many) 관계를 정의하는 패턴입니다.
  • 파이썬에서의 활용: GUI 애플리케이션의 위젯 상태 변경, 이벤트 핸들링, 주식 가격 변동 알림 시스템 등 한 객체의 상태 변화가 여러 객체에 영향을 미치는 경우에 사용됩니다. 파이썬에서는 events 라이브러리를 사용하거나, __setattr__ 같은 매직 메서드를 통해 구현할 수 있습니다.
  • 구현 예시:
    class Subject:
        def __init__(self):
            self._observers = []
    
        def attach(self, observer):
            self._observers.append(observer)
    
        def notify(self, data):
            for observer in self._observers:
                observer.update(data)
    
    class Observer:
        def update(self, data):
            print(f"Received update: {data}")
    
    subject = Subject()
    observer1 = Observer()
    subject.attach(observer1)
    subject.notify("State changed!")
    

4. 전략 패턴 (Strategy Pattern)

  • 개념: 알고리즘군(群)을 정의하고, 각 알고리즘을 별도의 클래스에 캡슐화하여 런타임에 이들을 교체할 수 있게 하는 패턴입니다.
  • 파이썬에서의 활용: 결제 시스템에서 여러 결제 방식(신용카드, PayPal 등)을 처리하거나, 데이터 압축 방식(ZIP, GZIP 등)을 선택적으로 적용하는 경우에 유용합니다. 파이썬에서는 함수를 일급 객체(First-class Citizen)로 다루므로, 함수를 직접 전략으로 전달하여 더 간결하게 구현할 수 있습니다.
  • 구현 예시:
    def standard_shipping(order):
        return order.price + 5
    
    def express_shipping(order):
        return order.price + 10
    
    class Order:
        def __init__(self, price, shipping_strategy):
            self.price = price
            self.shipping_strategy = shipping_strategy
    
        def calculate_total(self):
            return self.shipping_strategy(self)
    
    order1 = Order(100, standard_shipping)
    print(order1.calculate_total()) # 105
    

5. 데코레이터 패턴 (Decorator Pattern)

  • 개념: 객체의 기능을 동적으로 확장하는 패턴입니다. 이는 상속 대신 컴포지션(Composition)을 사용하여 유연하게 기능을 추가합니다.
  • 파이썬에서의 활용: 로깅, 권한 확인, 성능 측정과 같이 기존 함수의 동작을 수정하지 않고 추가적인 기능을 덧붙일 때 매우 유용합니다. 파이썬의 @ 문법은 이 패턴을 언어 차원에서 지원하여 매우 강력하게 활용됩니다.
  • 구현 예시:이처럼 데코레이터는 기존 코드를 건드리지 않고도 새로운 기능을 손쉽게 추가할 수 있게 해줍니다.
    import time
    
    def timing_decorator(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            end_time = time.time()
            print(f"Function {func.__name__} took {end_time - start_time} seconds.")
            return result
        return wrapper
    
    @timing_decorator
    def my_function():
        time.sleep(1)
        print("Function executed.")
    
    my_function()
    

결론: 파이썬은 언어적 유연성 덕분에 디자인 패턴을 간결하고 자연스럽게 구현할 수 있습니다. 이러한 패턴을 이해하고 적재적소에 활용하면, 더 효율적이고 유지보수하기 쉬운 코드를 작성할 수 있습니다.

반응형