Javaでは、ビルダーパターンを使用して、多くのパラメーターを使用してクラスをインスタンス化するためのより読みやすい手段を提供できます。ビルダーパターンでは、名前付き属性を設定するメソッドを使用して構成オブジェクトを作成し、それを使用して別のオブジェクトを作成します。
Pythonで同等のものは何ですか?同じ実装を模倣する最良の方法はありますか?
Javaでは、ビルダーパターンを使用して、多くのパラメーターを使用してクラスをインスタンス化するためのより読みやすい手段を提供できます。ビルダーパターンでは、名前付き属性を設定するメソッドを使用して構成オブジェクトを作成し、それを使用して別のオブジェクトを作成します。
Pythonで同等のものは何ですか?同じ実装を模倣する最良の方法はありますか?
多くの場合、デザインパターンは組み込みの言語機能に置き換えることができます。
「多くのパラメータを使用してクラスをインスタンス化するための、より読みやすい「手段」が必要でした」とあなたは言います。Javaの場合:
ただし、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))
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の属性を設定および削除すると、エラーが発生します。
結論:ビルダーは、オブジェクトを使用する準備ができたときにオブジェクトに不変の状態を提供する、可変の状態の何かを渡すための優れた方法です。または、別の言い方をすれば、ビルダーは、不変の状態を保証しながら、属性セッターを提供するための優れた方法です。これは、機能パラダイムで特に価値があります。
@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()
このパターンを構築する必要性に出くわし、この質問に出くわしました。私はこの質問がどれほど古いかを理解していますが、他の人に役立つ場合に備えて、私のバージョンのビルダーパターンを追加することもできます。
デコレータを使用してビルダークラスを指定することは、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)
Javaのビルダーパターンは、次のバリアントを使用してPythonで簡単に実現できます。
MyClass(self, required=True, someNumber=<default>, *args, **kwargs)
ここでrequired
、およびsomeNumber
は、デフォルト値を使用して必要なパラメーターを表示し、次のような場合を処理しながら変数引数を読み取る例です。None
以前に可変引数を使用したことがない場合は、これを参照してください
ビルダーとコンストラクターは同じものではありません。ビルダーは概念であり、コンストラクターはプログラミング構文です。2つを比較する意味はありません。
したがって、コンストラクター、クラスメソッド、または特殊なクラスを使用してビルダーパターンを実装できることを確認してください。競合は発生せず、ケースに適した方を使用してください。
概念的には、ビルダーパターンは、構築プロセスを最終オブジェクトから切り離します。家を建てる実例を見てみましょう。建築業者は家を建てるために多くの道具や材料を使うかもしれませんが、最終的な家はそれが建てられた後にそれらの道具や余分な材料を置いておく必要はありません。
例:
woodboards = Stores.buy(100)
bricks = Stores.buy(200)
drills = BuilderOffice.borrow(4)
house = HouseBuilder.drills(drills).woodboards(woodboards).bricks(bricks).build()