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

Tom*_*zyk 41 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);
Run Code Online (Sandbox Code Playgroud)

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

Que*_*tin 78

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

  • 如果您的参数没有意义相关,您的函数可能会尝试同时服务于多个目的. (26认同)
  • 请注意,有许多未分离的参数会导致您的函数是否执行过多操作的问题. (13认同)
  • @dynamic可能.当你真正想要一个公共领域的集合时,`struct`很适合.对我来说,"class"是一个指示,可能是私人国家被管理.另一方面,`struct`意味着平坦,简单的数据,这正是这里所需要的. (6认同)
  • 有些人会,所以做两三个.或者为此方法创建一个特定的参数对象(它们是相关的,因为它们是此方法的参数). (3认同)
  • 是否有可能`script`应该是一个对象,你的每个参数都应该是属性,而`generate`应该是类中的一个方法? (2认同)

Ral*_*zky 44

把它们放进去 struct

创建一个结构

struct GenerateScriptParams { /* ... */ };
Run Code Online (Sandbox Code Playgroud)

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

使界面对调用者来说很好

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

GenerateScriptParams gsp;
gsp.net = true;
gsp.phone = false;
gps.extra = 10;
generate_script( gsp );
Run Code Online (Sandbox Code Playgroud)

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

GenerateScriptParams & GenerateScriptParams::setNet  ( bool val );
GenerateScriptParams & GenerateScriptParams::setTV   ( bool val );
GenerateScriptParams & GenerateScriptParams::setPhone( bool val );
// ... //
Run Code Online (Sandbox Code Playgroud)

然后调用代码可以写

generate_script( GenerateScriptParams()
    .setNet(true),
    .setPhone(false),
    .setExtra(10) );
Run Code Online (Sandbox Code Playgroud)

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

  • 因为我有一段时间没有做过任何C++,所以我最初将"GenerateScriptParams&GenerateScriptParams :: setNet"看作是两个"GenerateScriptParams"实例之间的一个"AND" - 这让我感到困惑.我个人更喜欢`GenerateScriptParams&GenerateScriptParams :: setNet`,因为在考虑函数的返回类型时,&符号绑定到类名. (33认同)

Ale*_*zzi 20

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

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

编辑

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

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

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

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

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

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

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

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

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

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

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

Pov*_*asB 13

或者您可以使用流畅的界面.它看起来像这样:

script my_script(mandatory, parameters);
my_script.net(true).tv(false).phone(true);
Run Code Online (Sandbox Code Playgroud)

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


izb*_*izb 8

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

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

例如

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);
Run Code Online (Sandbox Code Playgroud)

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