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

时间:2014-07-25 09:17:14

标签: c++ function argument-passing

我有一个看起来像这样的功能:

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)的向量,并将它们作为参数传递给此函数吗?

6 个答案:

答案 0 :(得分:77)

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

答案 1 :(得分:44)

将它们放在struct

创建结构

struct GenerateScriptParams { /* ... */ };

并将所有参数放在那里。您实际上可以通过实现默认构造函数来提供struct初始化的默认值,或者在C ++ 11中通过提供单个成员的默认初始化来提供默认值。然后,您可以更改不应默认的值。对于在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) );

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

答案 2 :(得分:19)

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

问题是这个结构有多少可重用?如果最终为一个函数调用了18个参数,那么它在您的设计中就不太对了。在进一步分析之后,您可能会发现这些参数可以分组在几个不同的类中,并且这些类可以聚合到一个单独的对象,该对象将是您的函数的输入。您可能还希望使用struct来构造类以保护数据。

修改

我将举一个小例子来描述为什么有几个类比一个单片结构更好。让我们开始计算您需要编写的测试以涵盖上述功能。输入有18个参数(3个布尔值)。因此,我们需要至少15次测试才能验证输入(假设值没有互连)。

如果没有实施,测试的总数就无法计算,但我们可以了解其大小。让下限所有输入都可以视为布尔值,可能的组合数是2 ^ 18所以围绕 262000测试

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

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

但更重要的是,测试的数量会崩溃,比如四个一组(每个对象4,4,4和4个参数),测试总数只有:

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

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

那么,更具成本要求的是什么?写几千个测试或几个类?

显然,这是一个粗略的简化,然而,它将成为问题核心的基础。 杂乱的界面不仅仅是风格问题,也不仅仅是开发人员的不方便,也是制作高质量代码的真正障碍

这是我作为一名专业开发人员在我的职业生涯中学到的最重要的一课:"大班和胖接口是邪恶的"。这只是我对单一责任原则的启发式版本(我注意到SRP可能很难做到正确,单一责任似乎是合理的,在一小时编码后它可能不太相同,所以我使用了一些启发式规则来帮助我恢复我的初始选择。)

答案 3 :(得分:13)

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

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

如果您具有指定参数的默认值,或者允许其具有部分构造的脚本,则此选项适用。

答案 4 :(得分:8)

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

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

E.g。

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);

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

答案 5 :(得分:1)

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

这可以帮助您构建数据。