传递单个对象与传递多个参数

时间:2016-04-28 03:55:18

标签: design-patterns parameters method-call parameter-object

假设我有以下

Class A {
    Foo getFoo();
    Bar getBar();
    Baz getBaz();
}

我需要定义一个使用一个对象的doStuffFooBar并执行某些操作的函数Baz

我在实现doStuff的哪种方法更好(假设将doStuff放在类A中不合适)

方法A

void doStuff(Foo foo, Bar bar, Baz baz)
{ 
    //some operation
}

方法B

void doStuff(A a)
{
    Foo foo = a.getFoo();
    Bar bar = a.getBar();
    Baz baz = a.getBaz();
    //some operation
}

据我所知, (+专业, - 缺点)

方法A

+确切地说明doStuff()参数

的确切参数

- 易受长参数列表的影响,更容易受到用户错误的影响

方法B

+简单易用的方法

+似乎更具可扩展性(?)

- 对课程A

创建不必要的依赖

任何人都可以分享对这两种方法的利弊的额外见解吗?

4 个答案:

答案 0 :(得分:23)

方法A(裸参数)总是具有

的优点
  • 它要求方法作者输入less,因为它们不必实现参数对象,
  • 它要求方法调用者输入less,因为它们不必实例化参数对象
  • 它表现更好,因为不需要构造参数对象并收集垃圾
  • 读者可以单独从方法签名中看到各个参数是什么(但这是一把双刃剑;见下文)

方法B(Parameter Object)具有优势

  • 参数具有域意义作为一个组,因此可以为参数对象指定一个解释该含义的名称,从而使读者不必阅读和理解该组的每个成员以及它们之间的关系
  • 参数列表用于多个方法,因此在每个方法中使用参数对象可以减少重复
  • 参数列表中的值作为一组在多个方法中传递,当它们作为单个参数对象传递时更容易
  • 某些值组合无效;参数对象可以阻止那些组合
  • 某些值是可选的,可以由参数对象提供,而不是(取决于您的语言)默认参数值或重载方法
  • 有多个相同类型的参数,更有可能使值交换错误(尽管参数对象在这种情况下不是更好,如果它 有一个构造函数与方法具有相同的参数列表)

参数对象引入了一个新的依赖关系,调用者和被调用者依赖它并不是一个缺点,因为它是一个没有自己依赖的简单类。

因此,参数对象是

  • 对于单个参数几乎从来没有价值,有时候对于双参数方法是值得的(例如,Point通常比x,y更好),有时候没有,并且对三个或更多参数越来越有帮助
  • 在更多方法使用相同参数列表时越来越有用

答案 1 :(得分:5)

Parameter Object确实提供了一种很好的方法来封装related参数,以减少任何方法或构造函数的总参数计数。 应该非常小心地确保参数对象确实包含真正相关的参数。

实际上,根据您正在处理的parameter types,有多种方法可以解决此问题。 如果您正在处理一般类型的参数,例如多个StringInt,并且客户端有可能实际传递错误的参数序列,那么它通常更有意义创建custom types即。使用可能的值创建enum。这可以为您的参数提供良好的编译时检查。 它们的另一个好用途是你可以将它们用于return来自函数的复杂值。请参阅here

我需要花费很多时间的另一种方法是检查并查看doStuff方法完成的工作是否可以分解为具有较少依赖性的简单方法。

主要是我尝试遵循Bob Martin建议的最多三个参数。嗯,他实际上说它应该大多不超过一个! 任何增加都应该有正当理由。 请参阅这本优秀的书:Clean Code

答案 2 :(得分:1)

David和Som的答案都有很多值得考虑的信息。我将添加以下内容:

与许多设计模式一样,要做什么的决定取决于选择之间的连续性,各有利弊。并非总是有一个正确的答案-往往取决于您想享受哪些优点,以及您愿意冒险哪些缺点。

以我的经验,当您具有总是一起旅行的相关值时,转到DTO会很有帮助。大卫很好地描述了这种方法的优点。我看到的这种方法的另一个缺点是,随着DTO的增长,您可能会给方法添加不必要的依赖关系。

例如,方法A,B,C和D带有Foo,Bar和Baz,因此将这些参数组合到DTO中非常好。然后方法A和B需要使用Quux-您是否将Quux添加到DTO中以迫使C和D承担未使用的依赖关系?测试C和D时,您会为Quux传递什么值?当新开发人员使用方法C和D时,Quux的存在会引起混乱吗?在比较方法A和方法C时,是否清楚应该如何定义Quux?

当所有方法最初都需要Foo,Bar和Baz,但是某些方法不再需要这些值时,会出现类似情况。

我观察到一种经验,一个团队将DTO传递给另一团队,并付出巨大的努力来正确填充和同步该DTO中的信息,而实际上所需的只是一个可以传递的值。琐碎的。

除非值总是在一起,否则您可能会产生混乱,增加测试负担和额外的开发工作。如果这些值始终在一起,则DTO可以提供清晰度,减少重复,简化一致性等。

答案 3 :(得分:0)

请考虑具有地址 CurrentInvoice 客户。哪个更正确-

SendInvoiceToAddress(Invoice invoice, Address adress);

SendInvoiceToAddress(Customer customer);

我都认为。或换种说法-确实取决于您的应用。

如果每张发票(按定义)属于单个客户,那是一回事。这意味着您的方法存在于CustomerInvoiceSender类(或诸如此类)内。这完全属于客户领域。您想每月发送该月的发票(别无其他)。

如果您想向多个地址发送多张发票(出于任何目的,不一定非要给客户),那么那就完全不一样了。它也可能存在于InvoiceSender类中(或类似的东西)。它也与客户域无关。在这种情况下,客户只是发货的一小盒发票。还值得注意的是,在这种情况下,您可能需要接口而不是具体的,因为客户的发票,并且说公司的发票可能是两个截然不同的类,只是碰巧共享一个公共接口(在这种情况下为“几个属性”)。