Python Builder パターンを実装するさまざまな方法

Zeeshan Afridi 2023年6月21日
  1. Builder パターン、その重要性と機能
  2. Python で Builder パターンを実装するさまざまな方法
  3. Python での Builder パターンの長所と短所
Python Builder パターンを実装するさまざまな方法

Builder パターンは、オブジェクトの作成をその表現から切り離すことを可能にするパターンです。 このパターンは、継承なしで複雑なオブジェクトを作成および構成できます。これは強力ですが、柔軟性に欠けるアプローチです。

ビルダー パターンを利用して、実行時にクラスのさまざまな実装を提供したり、ユーザーがソース コードにアクセスせずに新しいオブジェクトを構築できるようにしたりすることもできます。

このチュートリアルでは、ビルダー パターンとその仕組みについて説明します。 また、Python ビルダー パターンを実装するさまざまな方法も示します。

Builder パターン、その重要性と機能

ビルダー パターンは、複雑なオブジェクトの構築を段階的に作成できるようにするソフトウェア設計パターンであり、新しいレベルの詳細または機能を提供します。

このパターンは、さまざまなソフトウェア コンポーネントを組み合わせて完全なシステムを形成する必要があるソフトウェア開発でよく使用されます。

ビルダー パターンは、テーブルや椅子などの単純なオブジェクトから、コンピューターや飛行機などのより複雑なシステムまで、あらゆるものを作成するために利用できます。 また、特定の順序で作成する必要があるオブジェクトを作成する場合にも役立ちます。

Python 標準ライブラリには builder というモジュールが用意されており、Builder パターンを簡単に使用できます。 ビルダー モジュールは、連携してオブジェクトを作成する BuilderDirector の 2つのクラスを提供します。

Builder クラスはオブジェクト パーツの作成を担当し、Director クラスはコンポーネントを最終的なオブジェクトに組み立てるために使用されます。

Builder パターンを使用するには、最初に Builder オブジェクトを作成します。これを使用して、オブジェクトのパーツを作成します。 それから Director オブジェクトを作成し、これを使用してピースを最終的なオブジェクトに組み立てます。

最後に、最終的なオブジェクトを返す Director オブジェクトで build() メソッドを呼び出すことができます。

Python で Builder パターンを実装するさまざまな方法

Python で Builder パターンを実装するには、次の 2つの方法があります。

  1. __init__ メソッド
  2. __new__ メソッド

__init__ メソッドを使用する

__init__ メソッドは、Python で Builder パターンを実装する最も一般的な方法です。 オブジェクトの作成時に呼び出され、その属性の値を設定できます。

__new__ メソッドを使用する

__new__ メソッドはあまり一般的ではありませんが、より強力です。 クラスの作成時に __new__ メソッドが呼び出され、オブジェクト自体を作成できるようになります。

これは、オブジェクトの作成方法を制御でき、クラスと同じ型ではないオブジェクトを作成できることを意味します。

Python での Builder パターンの長所と短所

ビルダー パターンには、以下に示すいくつかの利点があります。

  • ビルダー パターンの本質的な利点は、複雑なオブジェクトを段階的に作成し、新しいレベルの詳細または機能を提供できることです。
  • これにより、複雑なオブジェクトをより簡単かつ効率的に作成できます。
  • ビルダーパターンは、さまざまなビルダーがさまざまなタイプのオブジェクトを作成できるため、高度な柔軟性を可能にするという利点もあります。

ビルダー パターンの使用にもいくつかの欠点があります。

  • 1つは、各ステップを順番に実行する必要があるため、オブジェクトの作成に非常に時間がかかることです。
  • もう 1つの欠点は、ビルダー パターンが非常に複雑になり、多くのコードを記述する必要があることです。

コード例:

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

Builder パターンは、構造と表現を分離することによって複雑なオブジェクトを構築するのに役立つ設計パターンでもあることに注意してください。 この分離により、同じ構築プロセスから異なる表現を作成できます。

著者: Zeeshan Afridi
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