命名参数会替换生成器模式吗?


20

当使用支持命名和可选参数的语言时,构建器模式不再具有实际用途吗?

建造者:

new Builder(requiredA, requiredB).setOptionalA("optional").Build();

可选/命名参数:

new Object(requiredA, requiredB, optionalA: "optional");

3
您如何处理20个可选参数?直到变大为止,Builder都不需要解决问题。至此,您已经描述了两个构造函数(我不会为那么小的问题构建一个Builder)。

1
即使有可选参数-如果构造函数有两个以上参数,我还是更喜欢使用值对象来封装配置。流畅的界面和构建器也是如此:大于3的任何值都将替换为值对象。
Thomas Junk

Answers:


21

当您的对象需要大量的自变量/依赖性时,或者您希望允许使用许多不同的方式构造对象时,构建器将非常有用。

我可以想象,有人可能想在3D游戏中“构建”这样的对象:

// Just ignore the fact that this hypothetical god class is coupled to everything ever
new ObjectBuilder(x, y, z).importBlenderMesh("./meshes/foo")
                          .syncWithOtherPlayers(serverIP)
                          .compileShaders("./shaders/foo.vert", "./shaders/foo.frag")
                          .makeDestructibleRigidBody(health, weight)
                          ...

我认为与使用可选参数相比,使用我刚刚构建的构建器方法更容易理解该示例:

new Object(x, y, z, meshType: MESH.BLENDER,
                    meshPath: "./meshes/foo",
                    serverToSyncWith: serverIP,
                    vertexShader: "./shaders/foo.vert",
                    physicsType: PHYSICS_ENGINE.RIGID_DESTRUCTIBLE,
                    health: health,
                    weight: weight)
                    ...

特别是,构建器方法名称所隐含的信息必须替换为更多的参数,而忘记一组紧密相关的参数中的一个参数要容易得多。实际上,片段着色器丢失了,但是除非您知道要寻找它,否则您不会注意到这一点。


当然,如果您的对象仅需要一到五个参数来构造,则无论您是否具有命名/可选参数,都无需涉及到构建器模式。


我不赞成你的观点。如果构建器方法名称如此美妙,那么您也可以将它们用作参数名称。如果参数密切相关,请将其放入小型对象构造函数中。
user949300

@ user949300我认为您已经错过了重点的要点,那就是此处的构建器方法描述了如果只有一堆可选参数时丢失的参数之间的关系。在Ixrec的构建器示例中,“健康”和“重量”显然在逻辑上是可破坏主体设置的一部分,但是在可选参数版本中,关系l丢失了。
Jules

1
否,如果可选参数之一是(body:new DestructibleRigidBody(health,weight),...)
Weyland Yutani

@Jules。韦兰德(Weyland)所说的话-为体重和身高建立一个名字很好的构造函数。
user949300

8

除了Ixrec所说的以外,名为参数的构造函数或方法将不允许您将对象置于待构造状态,在构建状态下仍可以对其进行修改。这就是Builder的优点,您可以在其中将其构造的一部分委托给不同的方法或类:

var myThingBuilder = new ThingBuilder("table");
myThingBuilder.setAttribute(Attributes.Legs, 4);

inventoryManager.setPrices(myThingBuilder);

// inventory manager
var availableCheapestMaterial = getMaterial();
myThingBuilder.setMaterial(availableCheapestMaterial);

基本上,您还可以将构建器扔到系统上,直到准备构建最终对象为止,从而可以减少构建器-消费者需要具备的知识量。


我不明白你的最后一段。如果您“将构建器扔到系统周围直到准备就绪”,那么它将对系统有太多的了解。您的ThingBuilder知道属性,被InventoryManager神秘地修改,并且知道材料。看不出这是如何减少知识的。
user949300

@ user949300想一想,如果构建器无法跨系统运行,那会是怎样的情况。您将不得不上一堂具有巨大扇出因子的课程,而这对于构建Thing只是必要的。(当然,假设您的课程没有集中所有知识,那么我们首先要避免这样做。)现在,如果该课程还有其他责任,您将创建一个庞大的课程,在SOLID中打破S 。如果这是唯一的责任,那么您就可以自己成为ThingBuilder。
Alpha

1

这取决于您对构建器的处理方式。

如果仅使用构建器来设置(并改变)对象属性和(延迟)对象创建,则可以将其替换为命名参数。

替换构建器时,您可能具有@Ixrec改进的可读性/使用权衡(或者可能没有,这取决于您对构建器的操作)。

但是,如果您的构建器不仅仅是保留属性,而且每个构造步骤都涉及逻辑,则无法替换它。

MockBuilder是一个示例,其中不能将其替换为命名的params。从页面:

创建步骤的逻辑不能替换为命名参数


-5

使用不可变对象时,构建器模式至关重要。使用不可变对象有很多好处,尤其是在并行环境(例如线程)中使程序更健壮地执行时


3
是的,构建器非常适合处理复杂的不可变对象(甚至是可变对象),您需要确保该对象处于一致状态后才能使用。也就是说new Integer(42)new BigDecimal("42.000")new String("foobar")都是不可变对象的所有构造函数……好吧,对于这些实例,构造器将不必要地复杂。因此,当构造器也可以正常工作时,构造器对于使用不可变对象并不是必不可少的。

是的,构造函数可以与不可变对象一起使用,而不会被拒绝。但是最初的问题是,除了可选参数之外,Builder模式是否还有实际用途,我的回答是澄清这一点。
codedabbler 2015年

2
最初的问题没有提及不可变对象。它询问有关命名参数以及与Builders的关系。您的答案是关于构建器及其与不可变对象的关系。我很难看到您的答案如何回答问题。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.