35

假设我有以下

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

我需要定义一个函数doStuff,它使用一个对象的Foo, Bar,并做一些事情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

4 回答 4

45

方法 A(裸参数)始终具有以下优点:

  • 它要求方法作者输入更少,因为他们不必实现参数对象,
  • 它要求方法调用者键入更少,因为它们不必实例化参数对象
  • 它性能更好,因为不必构造参数对象和收集垃圾
  • 读者可以仅从方法签名中看到各个参数是什么(但这是一把双刃剑;见下文)

方法 B(参数对象)在以下情况下具有优势

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

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

所以,参数对象是

  • 几乎不值得使用单个参数,有时值得使用双参数方法(例如 Point 通常比 x、y 更好),有时则不值得,并且对三个或更多参数的帮助越来越大
  • 当更多方法使用相同的参数列表时越来越有用
于 2016-04-28T04:43:08.950 回答
7

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

实际上有多种方法可以解决这个问题,具体取决于parameter types您正在处理的问题。如果您正在处理的参数是一般类型的参数,例如多个Strings 或Ints,并且客户端实际上可能会传入错误的参数序列,那么创建custom typesie 通常更有意义。创造enum可能的价值。这可以为您的参数提供良好的编译时间检查。它们的另一个好用处是您可以将它们用于return函数中的复杂值。见这里

我经常使用的另一种方法是检查并查看该方法所做的工作是否doStuff被分解为具有较少依赖性的更简单的方法。

原则上,我尝试遵循 Bob Martin 最多三个参数的建议。嗯,他居然说应该最多不超过一个!任何增加都应该有正当理由。参考这本优秀的书:清洁代码

于 2016-04-28T07:41:21.830 回答
2

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 可以提供清晰性、减少重复、简化一致性等。

于 2019-02-25T18:20:34.790 回答
1

考虑一个具有AddressCurrentInvoice的客户。哪个更正确——

SendInvoiceToAddress(Invoice invoice, Address adress);

或者

SendInvoiceToAddress(Customer customer);

我认为两者兼而有之。或者换一种说法——这真的取决于你的应用程序。

如果每张发票(根据定义)属于单个客户,那是一回事。这意味着您的方法存在于 CustomerInvoiceSender 类(或类似的东西)中。这完全属于客户领域。每个月您都想发送该月的发票(仅此而已)。

如果您想将多张发票发送到多个地址(不一定为客户,出于任何目的),那是一个完全不同的故事。它可能也存在于 InvoiceSender 类(或类似的东西)中。它也与客户域无关。在这种情况下,客户只是一小部分正在发货的发票。另外值得注意的是,在这种情况下,您可能需要接口而不是具体的,因为客户的发票并说公司的发票可能是两个非常不同的类,它们恰好共享一个公共接口(在这种情况下为“几个属性”)。

于 2019-03-06T14:31:59.460 回答