파이썬 빌더 패턴을 구현하는 다양한 방법

Zeeshan Afridi 2023년6월21일
  1. 빌더 패턴, 그 중요성과 작동
  2. Python에서 빌더 패턴을 구현하는 다양한 방법
  3. Python 빌더 패턴의 장단점
파이썬 빌더 패턴을 구현하는 다양한 방법

빌더 패턴은 객체 생성을 해당 표현과 분리할 수 있는 패턴입니다. 이 패턴은 강력하지만 융통성이 없는 접근 방식인 상속 없이 복잡한 개체를 만들고 구성할 수 있습니다.

빌더 패턴은 또한 런타임 시 클래스의 다양한 구현을 제공하거나 사용자가 소스 코드에 액세스하지 않고 새 객체를 구성할 수 있도록 하는 데 활용될 수 있습니다.

이 튜토리얼은 빌더 패턴과 작동 방식에 대해 교육합니다. 또한 Python 빌더 패턴을 구현하는 다양한 방법을 보여줍니다.

빌더 패턴, 그 중요성과 작동

빌더 패턴은 복잡한 객체의 구성을 단계별로 생성하여 새로운 수준의 세부 사항이나 기능을 제공하는 소프트웨어 설계 패턴입니다.

이 패턴은 서로 다른 소프트웨어 구성 요소를 결합하여 완전한 시스템을 형성해야 하는 소프트웨어 개발에서 자주 사용됩니다.

빌더 패턴은 테이블이나 의자와 같은 단순한 개체에서 컴퓨터나 비행기와 같은 보다 복잡한 시스템에 이르기까지 무엇이든 만드는 데 활용할 수 있습니다. 또한 특정 순서로 생성해야 하는 개체를 생성하는 데 유용합니다.

Python 표준 라이브러리는 빌더 패턴을 쉽게 사용할 수 있도록 빌더라는 모듈을 제공합니다. 빌더 모듈은 개체를 생성하기 위해 함께 작동하는 빌더디렉터라는 두 클래스를 제공합니다.

Builder 클래스는 개체 부분을 생성하는 역할을 하며 Director 클래스는 구성 요소를 최종 개체로 조립하는 데 사용됩니다.

빌더 패턴을 사용하려면 먼저 빌더 개체를 만들고 개체의 일부를 만드는 데 사용합니다. 그런 다음 조각을 최종 개체로 조립하는 데 사용하는 Director 개체를 만듭니다.

마지막으로 최종 개체를 반환하는 Director 개체에서 build() 메서드를 호출할 수 있습니다.

Python에서 빌더 패턴을 구현하는 다양한 방법

Python에서 빌더 패턴을 구현하는 두 가지 방법이 있습니다.

  1. __init__ 방법
  2. __new__ 메소드

__init__ 방법 사용

__init__ 메서드는 Python에서 빌더 패턴을 구현하는 가장 일반적인 방법입니다. 객체가 생성될 때 호출되어 속성 값을 설정할 수 있습니다.

__new__ 방법 사용

__new__ 방법은 덜 일반적이지만 더 강력합니다. 클래스가 생성될 때 __new__ 메서드가 호출되어 객체 자체를 생성할 수 있습니다.

즉, 객체가 생성되는 방식을 제어할 수 있고 클래스와 동일한 유형이 아닌 객체를 생성할 수도 있습니다.

Python 빌더 패턴의 장단점

빌더 패턴에는 다음과 같은 몇 가지 장점이 있습니다.

  • 빌더 패턴의 본질적인 장점은 복잡한 객체를 단계별로 생성하여 새로운 수준의 세부 사항이나 기능을 제공할 수 있다는 것입니다.
  • 이렇게 하면 복잡한 개체를 훨씬 간단하고 효율적으로 만들 수 있습니다.
  • 빌더 패턴은 다른 빌더가 다른 유형의 객체를 생성할 수 있으므로 높은 수준의 유연성을 허용한다는 점에서도 유리합니다.

빌더 패턴을 사용하는 데에도 몇 가지 단점이 있습니다.

  • 하나는 각 단계를 순서대로 수행해야 하므로 개체를 만드는 데 시간이 많이 걸릴 수 있다는 것입니다.
  • 또 다른 단점은 빌더 패턴이 상당히 복잡할 수 있고 작성하는 데 많은 코드가 필요하다는 것입니다.

코드 예:

from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Any


class Builder(ABC):
    @property
    @abstractmethod
    def product(self) -> None:
        pass

    @abstractmethod
    def produce_a(self) -> None:
        pass

    """
    This class will provide specific working of the building steps by using the builder interface.
    """


class ConcreteBuilder1(Builder):
    def __init__(self) -> None:
        """This fresh builder instance will contain a blank product object, which is
        used in further assembly.
        """
        self.reset()

    def reset(self) -> None:
        self._product = Product1()

    @property
    def product(self) -> Product1:

        product = self._product
        self.reset()
        return product

    def produce_a(self) -> None:
        self._product.add("PartA")


class Product1:
    """
    When your products are complex and demand extensive configuration, it's possible to use the Builder pattern only.
    """

    def __init__(self) -> None:
        self.parts = []

    def add(self, part: Any) -> None:
        self.parts.append(part)

    def list_parts(self) -> None:
        print(f"The Product parts: {', '.join(self.parts)}", end="")


class Director:
    """
    The client can control builders directly, so the Director class is
     optional. In this case, the Director is only responsible for executing the building steps in a specific sequence.
    """

    def __init__(self) -> None:
        self._builder = None

    @property
    def builder(self) -> Builder:
        return self._builder
        """
    The Director can construct several product variations using the same
    building steps.
    """

    @builder.setter
    def builder(self, builder: Builder) -> None:
        self._builder = builder

    def build_minimal_viable_product(self) -> None:
        self.builder.produce_a()

    def build_full_featured_product(self) -> None:
        self.builder.produce_a()


if __name__ == "__main__":

    director = Director()
    builder = ConcreteBuilder1()
    director.builder = builder

    print("Our Standard primary product:")
    director.build_minimal_viable_product()
    builder.product.list_parts()

    print("\n")

    print("Our Standard full-featured product:")
    director.build_full_featured_product()
    builder.product.list_parts()

    print("\n")
    # We can also use the Builder pattern without a Director class.
    print("Our Custom product: ")
    builder.produce_a()
    builder.product.list_parts()

출력:

Our Standard primary product:
The Product parts: PartA

Our Standard full-featured product:
The Product parts: PartA

Our Custom product: 
The Product parts: PartA

빌더 패턴은 구성과 표현을 분리하여 복잡한 객체를 빌드하는 데 도움이 되는 디자인 패턴이기도 합니다. 이러한 분리를 통해 동일한 구성 프로세스에서 다른 표현을 만들 수 있습니다.

Zeeshan Afridi avatar Zeeshan Afridi avatar

Zeeshan is a detail oriented software engineer that helps companies and individuals make their lives and easier with software solutions.

LinkedIn