【问题标题】:Extend an existing API: Use default argument or wrapper function?扩展现有 API:使用默认参数或包装函数?
【发布时间】:2009-06-02 17:38:58
【问题描述】:

我有一个现有的方法(或一般的函数),我需要增加额外的功能,但我不想在代码的其他地方破坏该方法的任何使用。示例:

int foo::bar(int x)
{
 // a whole lot of code here
 return 2 * x + 4;
}

在代码库中被广泛使用。现在我需要将 4 变成一个参数,但是任何已经调用 foo::bar 的代码仍然应该收到它所期望的。我是否应该扩展并重命名旧方法并将其包装成一个新方法,例如

int foo::extended_bar(int x, int y)
{
 // ...
 return 2 * x + y;
}

int foo::bar(int x)
{
 return extended_bar(x,4);
}

或者我应该在头文件中声明一个默认参数,比如

int bar(int x, int y=4);

只是扩展功能

int foo::bar(int x, int y)
{
 // ...
 return 2 * x + y;
}

每种变体的优缺点是什么?

【问题讨论】:

  • 对我来说似乎是风格而不是实质。两者都是可行的解决方案。

标签: c++ backwards-compatibility


【解决方案1】:

我通常使用包装函数(大部分时间通过重载)而不是默认参数。

原因是向后兼容有两个级别

  1. 具有源代码级向后兼容性意味着您必须重新编译调用代码而不作任何更改,因为新的函数签名与旧函数签名兼容。两者都可以达到这个水平;默认值和包装器/重载。

  2. 一个更强的级别是二进制级别向后兼容性,它甚至可以在不重新编译的情况下工作,例如当您无权访问调用代码时。想象一下,你以二进制形式部署你的函数,比如在一个 DLL 等中。在这种情况下,签名必须完全相同才能使其工作,而默认值不是这种情况——它们会破坏这种级别的兼容性.

包装函数的另一个优点是 - 如果您的应用程序有任何类型的日志记录 - 您可以在旧函数中转储一个警告,该函数将在未来版本中过时,建议使用新函数。

【讨论】:

    【解决方案2】:

    我使用 C++ 的时间越长,我就越不喜欢默认函数参数。我无法确定我不喜欢的任何具体原因,但我发现如果我使用它们,我几乎总是会在以后删除它们。所以我的(主观)投票支持新的命名函数——名称当然可以与旧函数的名称相同。

    【讨论】:

      【解决方案3】:

      我个人认为,隐性行为是万恶之源(之一)。

      任何让维护者或调用者混淆被调用目标的身份的东西都应该有非常充分的理由,尤其是当它是主要 API 的一部分时。

      因此,我强烈反对使用默认操作的选项。

      此外,我相信如果可以使用不同数量的参数调用某个函数,那么这两个版本的函数本质上会有所不同,或者它们做得太多。区别可能应该在名称中,并且应该比“_extended”更有意义

      【讨论】:

      • 我同意你的最后一段,它应该是 bar_with_adjustable_summand(x,y) 或类似的。但是,如果我需要在其他未更改的函数中控制附加参数,那么关于您的第一个问题的解决方案是什么?
      猜你喜欢
      • 1970-01-01
      • 2017-09-18
      • 1970-01-01
      • 2015-05-16
      • 1970-01-01
      • 1970-01-01
      • 2018-08-23
      • 2021-11-15
      • 2015-07-22
      相关资源
      最近更新 更多