74

Javaでは、ビルダーパターンを使用して、多くのパラメーターを使用してクラスをインスタンス化するためのより読みやすい手段を提供できます。ビルダーパターンでは、名前付き属性を設定するメソッドを使用して構成オブジェクトを作成し、それを使用して別のオブジェクトを作成します。

Pythonで同等のものは何ですか?同じ実装を模倣する最良の方法はありますか?

4

6 に答える 6

119

多くの場合、デザインパターンは組み込みの言語機能に置き換えることができます。

ユースケース

「多くのパラメータを使用してクラスをインスタンス化するための、より読みやすい「手段」が必要でした」とあなたは言います。Javaの場合:

[A]ビルダーパターンのユースケースは、ビルドされるオブジェクトのコンストラクターが非常に多くのパラメーターをとらなければならない場合です。このような場合、クラスのコンストラクターに渡す引数の長いリストを呼び出し元に負担させるよりも、ビルダーオブジェクト(、、、 ..など)にsetMaxTemperature(int t)そのような構成パラメーターをまとめる方が便利なことがよくあります。setMinTemperature(int t)set

ビルダーパターンは必要ありません

ただし、Pythonは名前付きパラメーターをサポートしているため、これは必要ありません。クラスのコンストラクターを定義するだけです。

class SomeClass(object):
    def __init__(self, foo="default foo", bar="default bar", baz="default baz"):
        # do something

名前付きパラメーターを使用して呼び出します。

s = SomeClass(bar=1, foo=0)

Javaのビルダーの場合と同様に、引数を自由に並べ替えたり省略したりできることに注意してくださいset。ビルダーオブジェクトのメソッドの呼び出しを省略または並べ替えることができます。

__new__また、Pythonの動的な性質により、オブジェクトの構築(使用など)の自由度が増し、ビルダーパターンの他の使用法を置き換えることができることも述べておく価値があります。

しかし、本当にそれを使いたいのなら

collections.namedtuple構成オブジェクトとして使用できます。namedtuple()ボイラープレートクラスを作成しなくても、タプルを表す新しい型を返します。各パラメーターには名前が付けられています。結果のタイプのオブジェクトは、Javaビルダーと同様の方法で使用できます。(これを提案してくれたPaul McGuireに感謝します。)

StringBuilder

String関連するパターンはJavaのStringBuilderであり、これは段階的に(不変の)を効率的に構築するために使用されます。Pythonでは、これは。に置き換えることができますstr.join。例えば:

final StringBuilder sb = new StringBuilder();
for(int i = 0; i < 100; i++)
    sb.append("Hello(" + i + ")");
return sb.toString();

で置き換えることができます

return "".join(f"Hello({i})" for i in range(100))
于 2012-08-15T21:16:45.753 に答える
49

OPは、BuilderパターンをJava固有としてキャストすることにより、秋に向けて準備を整えました。そうではありません。これはGangofFourの本にあり、オブジェクト指向言語に関連する可能性があります。

残念ながら、Builderパターンに関するウィキペディアの記事でさえ十分な信用を与えていません。これは、コードの優雅さのためだけに役立つわけではありません。ビルダーパターンは、使用されるまで変更可能である必要がある不変オブジェクトを作成するための優れた方法です。不変の状態は関数型パラダイムで特に重要であり、BuilderをPythonの優れたオブジェクト指向パターンにします。

「 pythonで不変オブジェクトを作成する方法」から借用および変更されたcollections.namedtupleを使用したBuilder+ImmutableObjectの実装例を以下に示します。Builderはかなりシンプルにしています。ただし、呼び出しチェーンを可能にするためにBuilder自体を返すセッター関数を提供することもできます。または、ビルダーで@property構文を使用して、設定前に属性の有効性をチェックする属性セッターを提供することもできます。

from collections import namedtuple

IMMUTABLE_OBJECT_FIELDS = ['required_function_result', 'required_parameter', 'default_parameter']

class ImmutableObjectBuilder(object):
    def __init__(self, required_function, required_parameter, default_parameter="foo"):
        self.required_function = required_function
        self.required_parameter = required_parameter
        self.default_parameter = default_parameter

    def build(self):
        return ImmutableObject(self.required_function(self.required_parameter),
                               self.required_parameter,
                               self.default_parameter)

class ImmutableObject(namedtuple('ImmutableObject', IMMUTABLE_OBJECT_FIELDS)):
    __slots__ = ()

    @property
    def foo_property(self):
        return self.required_function_result + self.required_parameter

    def foo_function(self):
        return self.required_function_result - self.required_parameter

    def __str__(self):
        return str(self.__dict__)

使用例:

my_builder = ImmutableObjectBuilder(lambda x: x+1, 2)
obj1 = my_builder.build()
my_builder.default_parameter = "bar"
my_builder.required_parameter = 1
obj2 = my_builder.build()
my_builder.required_function = lambda x: x-1
obj3 = my_builder.build()

print obj1
# prints "OrderedDict([('required_function_result', 3), ('required_parameter', 2), ('default_parameter', 'foo')])"
print obj1.required_function_result
# prints 3
print obj1.foo_property
# prints 5
print obj1.foo_function()
# prints 1
print obj2
# prints "OrderedDict([('required_function_result', 2), ('required_parameter', 1), ('default_parameter', 'bar')])"
print obj3
# prints "OrderedDict([('required_function_result', 0), ('required_parameter', 1), ('default_parameter', 'bar')])"

この例では、3つのImmutableObjectsを作成しましたが、すべてパラメーターが異なります。呼び出し元に、ビルドされたオブジェクトの不変性を保証しながら、ビルダーの形式で変更可能な構成をコピー、変更、および渡す機能を提供しました。ImmutableObjectsの属性を設定および削除すると、エラーが発生します。

結論:ビルダーは、オブジェクトを使用する準備ができたときにオブジェクトに不変の状態を提供する、可変の状態の何かを渡すための優れた方法です。または、別の言い方をすれば、ビルダーは、不変の状態を保証しながら、属性セッターを提供するための優れた方法です。これは、機能パラダイムで特に価値があります。

于 2014-10-04T12:51:08.413 に答える
24

@MechanicalSnailに同意しません。ポスターで参照されているものと同様のビルダーの実装は、場合によってはまだ非常に役立つと思います。名前付きパラメーターでは、メンバー変数を設定するだけで済みます。もう少し複雑なことをしたいのなら、運が悪いです。私の例では、古典的なビルダーパターンを使用して配列を作成します。

class Row_Builder(object):
  def __init__(self):
    self.row = ['' for i in range(170)]

  def with_fy(self, fiscal_year):
    self.row[FISCAL_YEAR] = fiscal_year
    return self

  def with_id(self, batch_id):
    self.row[BATCH_ID] = batch_id
    return self

  def build(self):
    return self.row

それを使用する:

row_FY13_888 = Row_Builder().with_fy('FY13').with_id('888').build()
于 2014-03-05T18:04:51.093 に答える
8

このパターンを構築する必要性に出くわし、この質問に出くわしました。私はこの質問がどれほど古いかを理解していますが、他の人に役立つ場合に備えて、私のバージョンのビルダーパターンを追加することもできます。

デコレータを使用してビルダークラスを指定することは、Pythonでビルダーパターンを実装するための最も人間工学的な方法だと思います。

def buildermethod(func):
  def wrapper(self, *args, **kwargs):
    func(self, *args, **kwargs)
    return self
  return wrapper

class A:
  def __init__(self):
    self.x = 0
    self.y = 0

  @buildermethod
  def set_x(self, x):
    self.x = x

  @buildermethod
  def set_y(self, y):
    self.y = y

a = A().set_x(1).set_y(2)
于 2020-06-22T14:42:54.853 に答える
4

Javaのビルダーパターンは、次のバリアントを使用してPythonで簡単に実現できます。

MyClass(self, required=True, someNumber=<default>, *args, **kwargs)

ここでrequired、およびsomeNumberは、デフォルト値を使用して必要なパラメーターを表示し、次のような場合を処理しながら変数引数を読み取る例です。None

以前に可変引数を使用したことがない場合は、これを参照してください

于 2012-08-15T21:17:59.497 に答える
4

ビルダーとコンストラクターは同じものではありません。ビルダーは概念であり、コンストラクターはプログラミング構文です。2つを比較する意味はありません。

したがって、コンストラクター、クラスメソッド、または特殊なクラスを使用してビルダーパターンを実装できることを確認してください。競合は発生せず、ケースに適した方を使用してください。

概念的には、ビルダーパターンは、構築プロセスを最終オブジェクトから切り離します。家を建てる実例を見て​​みましょう。建築業者は家を建てるために多くの道具や材料を使うかもしれませんが、最終的な家はそれが建てられた後にそれらの道具や余分な材料を置いておく必要はありません。

例:

woodboards = Stores.buy(100)
bricks = Stores.buy(200)
drills = BuilderOffice.borrow(4)

house = HouseBuilder.drills(drills).woodboards(woodboards).bricks(bricks).build()
于 2020-06-30T08:07:58.267 に答える