将多个参数传递给函数的优雅方式

Posted

技术标签:

【中文标题】将多个参数传递给函数的优雅方式【英文标题】:Elegant way to pass multiple arguments to a function 【发布时间】:2014-09-17 02:22:57 【问题描述】:

我有一个看起来像这样的函数:

bool generate_script (bool net, bool tv, bool phone,
                        std::string clientsID,
                        std::string password,
                        int index, std::string number, 
                        std::string Iport, std::string sernoID,
                        std::string VoiP_number, std::string  VoiP_pass,
                        std::string target, int slot, int port, 
                        int onu, int extra, std::string IP, std::string MAC);

在我看来,它看起来很丑。处理这个问题的正确方法是什么?我应该创建几个具有不同数据类型(int、string 和 bool)的向量并将它们作为参数传递给这个函数吗?

【问题讨论】:

我不鼓励使用向量,结构会是更合适的解决方案。 还有 boost 命名参数,如果你有很多有用的默认值 将它们分组为一个或多个structs。另外,不要忘记对字符串使用const 引用类型,以防您不在函数中修改它,这样可以提高速度。 如果这个函数的唯一目的是处理一个结构,为什么不把它变成那个结构的方法呢? 似乎没有人提到的是它们都是按值传递的 :( 通过引用传递可以避免这么多的分配 【参考方案1】:

如果所有这些参数都有意义相关,请将它们打包到一个结构中。

【讨论】:

有些会,所以做两三个。或者为这个方法创建一个特定的参数对象(它们是相关的,因为它们是这个方法的参数)。 如果您的参数没有有意义的相关性,您的函数可能会尝试同时满足多个目的。 请注意,有很多很多未实现的参数会导致您的函数是否做太多事情的问题。 @dynamic 可能。 structs 非常适合您真的只想要公共字段的集合,就像这里一样。对我来说,class 表明可能有私有状态正在被管理。另一方面,struct 表示平坦、简单的数据,这正是这里所需要的。 是否也有可能script 应该是一个对象,你的每个参数都应该是属性,而generate 应该是类中的一个方法?【参考方案2】:

把它们放在struct

创建一个结构

struct GenerateScriptParams  /* ... */ ;

并把所有的参数放在那里。实际上,您也可以通过实现默认构造函数或在 C++11 中通过提供各个成员的默认初始化来为 struct 的初始化提供默认值。然后,您可以更改不应默认的值。对于在 C++ 中具有大量参数的函数调用,这种选择性地选择非默认参数是不可能的。

使界面对调用者友好

然而,用法有点难看,因为你必须创建一个临时名称对象,然后更改不应该默认的值,然后将对象传递给函数:

GenerateScriptParams gsp;
gsp.net = true;
gsp.phone = false;
gps.extra = 10;
generate_script( gsp );

如果您在多个不同的地方调用该函数,通过提供可以链接的变异成员函数来避免这种丑陋是有意义的:

GenerateScriptParams & GenerateScriptParams::setNet  ( bool val );
GenerateScriptParams & GenerateScriptParams::setTV   ( bool val );
GenerateScriptParams & GenerateScriptParams::setPhone( bool val );
// ... //

然后调用代码就可以写了

generate_script( GenerateScriptParams()
    .setNet(true),
    .setPhone(false),
    .setExtra(10) );

没有上述丑陋。这避免了只使用一次的命名对象。

【讨论】:

因为我有一段时间没有做过任何 C++,所以我最初将GenerateScriptParams & GenerateScriptParams::setNet 读作AND 介于两个GenerateScriptParams 实例之间——这让我很困惑。我个人更喜欢GenerateScriptParams& GenerateScriptParams::setNet,因为在考虑函数的返回类型时,与号绑定到类名。【参考方案3】:

我个人不相信将所有参数移动到一个结构中会使代码变得更好。您只需将污垢移到地毯下即可。当您要处理结构的创建时,您会遇到同样的问题。

问题是这个结构有多少可重用性?如果您最终为一个函数调用提供了 18 个参数,那么这在您的设计中并不完全正确。经过进一步分析,您可能会发现这些参数可以分组到几个不同的类中,并且这些类可以聚合到一个对象中,作为函数的输入。您可能还希望使用类而不是 struct 来保护您的数据。

编辑

我会给你一个小例子来描述为什么几个类比一个整体结构更好。让我们开始计算覆盖上述功能所需编写的测试。有 18 个参数作为输入(3 个布尔值)。因此,我们将需要至少 15 次测试来验证输入(假设这些值没有相互关联)。

如果没有实现,就无法计算出测试的总数,但我们可以对数量级有所了解。让所有输入的下限都可以视为布尔值,可能组合的数量为 2^18,因此大约 262000 个测试

现在,如果我们将输入分成几个对象会发生什么?

首先,验证输入的代码从函数移到每个对象的主体(并且可以重复使用)。

但更重要的是,测试的数量会崩溃,假设在四个一组(每个对象 4、4、4 和 4 个参数)中,测试的总数仅为:

2^4 + 2^4 + 2^4 + 2^4 + 2^4 = 80

第五个属性是由于对象自身的排列。

那么,什么对成本要求更高?编写数千个测试或更多类?

显然,这是一个粗略的简化,但是,它将成为问题核心的基础。 杂乱的界面不仅仅是样式问题或对开发人员的不便,它是生成高质量代码的真正障碍

这是我作为一名专业开发人员在职业生涯中学到的最重要的一课:“大类和胖接口是邪恶的”。这只是我对单一职责原则的启发式版本(我注意到 SRP 可能很难正确完成,单一职责似乎是合理的,经过一个小时的编码后可能不太一样,所以我使用了一些启发式规则来帮助我重新评估我最初的选择)。

【讨论】:

-1 创建“几个不同的类”来解决一个相对简单的问题是通过屋顶设计的。 @PaulManta 我已经详细说明了“几个不同的类”的好处 +1 ;这实际上是指出 OP 代码真正问题的唯一答案;在函数调用中使用太多参数使代码混乱的问题几乎不是“一个相对简单的问题”——即使是 EJ 中的 Bloch,2nd 也提到了这个项目作为使用例如的激励。构建器模式和专门的类。 ;对于“大类和胖接口是邪恶的”,我会给你另一个 +1 - 我在 OOP 编程生涯中学到了完全相同的课程(艰难的方式)。模块化取决于级联组合模型,而不是扁平模型!【参考方案4】:

或者您可以使用fluent interface。它看起来像这样:

script my_script(mandatory, parameters);
my_script.net(true).tv(false).phone(true);

如果您有指定参数的默认值或允许有部分构造的脚本,这适用。

【讨论】:

【参考方案5】:

忽略以某种方式更改函数或程序以减少参数数量的可能性或可​​取性...

我已经看到编码标准指定参数列表应该格式化多长时间,用于无法重构的情况。一个这样的例子是使用双缩进和每行一个参数(不适用于所有函数 - 仅适用于具有多行参数的函数)。

例如

bool generate_script (
        bool net,
        bool tv,
        bool phone,
        std::string clientsID,
        std::string password,
        int index,
        std::string number,
        std::string Iport,
        std::string sernoID,
        std::string VoiP_number,
        std::string  VoiP_pass,
        std::string target,
        int slot,
        int port,
        int onu,
        int extra,
        std::string IP,
        std::string MAC);

这里的重点是创建一致的布局,并查找所有具有大量参数的函数。

【讨论】:

我想我将按照 Quentin 的建议将这些参数打包到一个结构中。但是感谢您解释如何格式化这些函数。 前面应该放多少个空格,例如bool net,? 4个还是8个?如果为 4,它可能与 ... 中的实现无法区分。【参考方案6】:

这里有点晚了,但由于还没有人做过,我想指出这个问题的一个明显方面:对我来说,一个需要这么多参数的函数可能会做很多计算,所以作为第一步,考虑将其分解为更小的函数的可能性。

这应该可以帮助您构建数据。

【讨论】:

以上是关于将多个参数传递给函数的优雅方式的主要内容,如果未能解决你的问题,请参考以下文章

将多个参数传递给表值函数

将ajax请求中的多个参数传递给函数后面的C#代码

Django href 将多个参数传递给函数

将一个值列表而不是多个参数传递给函数?

将多个参数传递给后端 API reactjs

有没有办法将多个参数传递给jquery函数[重复]