在Java中,您可以使用builder pattern提供更具可读性的方法来实例化具有许多参数的类。在构建器模式中,构造一个配置对象,其中包含设置命名属性的方法,然后使用它来构造另一个对象。
Python中的等价物是什么?是模仿相同实现的最佳方式吗?
答案 0 :(得分:85)
设计模式通常可以替换为内置语言功能。
你说“我想要更具可读性”意味着“实例化一个包含许多参数的类”。在Java的情况下:
但是Python支持named parameters,所以这不是必需的。你可以定义一个类的构造函数:
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
方法的调用。
另外值得一提的是,Python的动态特性使您可以更自由地构建对象(使用__new__
等),这可以取代构建器模式的其他用途。
您可以使用collections.namedtuple
作为配置对象。 namedtuple()
返回一个表示元组的新类型,每个元组的参数都有一个给定的名称,而不必编写样板类。您可以使用与Java构建器类似的方式使用结果类型的对象。 (感谢Paul McGuire建议。)
StringBuilder
相关模式是Java的StringBuilder,它用于分阶段有效地构造(不可变的)String
。在Python中,可以用str.join
替换它。例如:
final StringBuilder sb = new StringBuilder();
for(int i = 0; i < 100; i++)
sb.append("Hello(" + i + ")");
return sb.toString();
可以替换为
return "".join("Hello({})".format(i) for i in range(100))
答案 1 :(得分:33)
OP通过将Builder模式转换为特定于Java的模式来设置自己的崩溃。不是。它位于Gang of Four's book中,可能与任何面向对象的语言相关。
不幸的是,即使Wikipedia article on the Builder pattern也没有给予足够的信任。它不仅仅对代码优雅有用。 构建器模式是创建不可变对象的好方法,这些对象在被使用之前需要是可变的。不可变状态在功能范例中尤其重要,使得Builder成为python的一个出色的面向对象模式
我在下面使用collections.namedtuple提供了一个示例Builder + ImmutableObject实现,从&#34; How to make an immutable object in python&#34;借用和修改。我保持Builder非常简单。但是,可以提供setter函数,返回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')])"
在这个例子中,我创建了三个ImmutableObjects,它们都有不同的参数。我已经让调用者能够以构建器的形式复制,修改和传递可变配置,同时仍然保证构建对象的不变性。在ImmutableObjects上设置和删除属性会引发错误。
底线:构建器是一种很好的方式来传递具有可变状态的东西,当你准备好使用它时,它提供了一个具有不可变状态的对象。或者,换句话说,构建器是提供属性设置器同时仍然确保不可变状态的好方法。这在功能范例中尤为重要。
答案 2 :(得分:12)
我不同意@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()
答案 3 :(得分:4)
使用以下变量:
可以在python中轻松实现Java中的构建器模式 MyClass(self, required=True, someNumber=<default>, *args, **kwargs)
其中required
和someNumber
是一个示例,用于显示具有默认值的必需参数,然后在处理可能存在None
如果您之前未使用过变量参数,请参阅this
答案 4 :(得分:2)
我刚遇到建立这种模式的需要,偶然发现了这个问题。我意识到这个问题有多老了,但是如果对其他人有用的话,不妨添加我的构建器模式版本。
我相信使用装饰器指定构建器类是在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)
答案 5 :(得分:1)
构建器和构造器不是同一件事,构建器是一个概念,构造器是一种编程语法。没有意义将两者进行比较。
因此,请确保您可以使用构造函数,类方法或专用类来实现构建器模式,没有冲突,请使用适合您情况的任何一种。
从概念上讲,构建器模式使构建过程与最终对象脱钩。以建造房屋的真实世界为例。建造者可能会使用很多工具和材料来建造房屋,但最终房屋不必在建造后就摆放这些工具和多余的材料。
示例:
woodboards = Stores.buy(100)
bricks = Stores.buy(200)
drills = BuilderOffice.borrow(4)
house = HouseBuilder.drills(drills).woodboards(woodboards).bricks(bricks).build()