传递多个参数到一个函数的优雅方式
我有一个看起来像这样的函数:
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);
在我看来,它看起来很丑。 处理这个问题的正确方法是什么? 我应该使用不同的数据types(int,string和bool)创build几个向量,并将它们作为parameter passing给此函数?
如果所有这些参数都是有意义的,请把它们包装在一个结构中。
把它们放在一个struct
创build一个结构
struct GenerateScriptParams { /* ... */ };
并把所有的参数放在那里。 您实际上可以为struct
的初始化提供默认值,也可以通过实现默认构造函数来提供,或者在C ++ 11中通过提供个别成员的默认初始化。 然后,您可以更改不应该默认的值。 对于在C ++中有大量参数的函数调用,这种非缺省参数的select是不可能的。
使接口对于调用者来说很好
然而,这个用法有点难看,因为你必须创build一个临时名称对象,然后改变那些不应该是默认的值,然后把这个对象传递给函数:
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) );
没有上述的丑陋。 这避免了仅使用一次的命名对象。
我个人不相信在一个结构中移动所有参数会使代码更好。 你只要把地毯下面的污垢 当你要处理结构的创build时,你有同样的问题。
问题是这个结构会有多less可重用? 如果你最终得到一个函数的18个参数,那么在你的devise中就不太合适。 经过进一步的分析,你可能会发现,这些参数可以分成几个不同的类,这些类可以聚合到一个单一的对象,这将是你的函数的input。 您可能也希望类可以结构化,以保护您的数据。
编辑
我会给你一个小例子来描述为什么几个类比单一的结构更好。 让我们开始计算你需要编写的testing来覆盖上面的函数。 有18个参数作为input(3布尔)。 所以我们至less需要15次testing才能validationinput(假设这些值是不相互关联的)。
如果没有实施,testing的总数是不可能计算的,但是我们可以有一个大小的想法。 让下界所有的input都可以视为布尔值,可能的组合数是2 ^ 18,所以在262000左右的testing 。
现在,如果我们将input分成几个对象,会发生什么?
首先,validationinput的代码从函数移到每个对象的主体(并且可以被重用)。
但更重要的是,testing的数量将会崩溃,比如说,在四人组中(每个对象有4,4,4和4个参数),testing的总数量只是:
2 ^ 4 + 2 ^ 4 + 2 ^ 4 + 2 ^ 4 + 2 ^ 4 = 80
第五个属性是由于对象与自己的排列。
那么,什么是更高的成本要求? 写上千个testing或更多的类?
显然,这是一个粗略的简化,但是,这将是问题核心的基础。 杂乱的界面不仅仅是风格上的问题,对于开发者来说也不方便,这是生成高质量代码的真正障碍 。
这是我作为专业开发人员在职业生涯中学到的最重要的一课:“大class和胖胖的界面是邪恶的”。 这只是我单一责任原则的启发式版本(我注意到,SRP可能会很棘手,但是一小时编码之后,单个责任似乎不太一样,所以我用了一些启发式规则,以帮助我重新初始化我的select)。
或者你可以使用stream畅的界面 。 它看起来像这样:
script my_script(mandatory, parameters); my_script.net(true).tv(false).phone(true);
如果您有指定参数的默认值,或者允许使用部分构build的脚本,则这是适用的。
忽略以某种方式更改函数或程序的可能性或合意性,以减less参数的数量…
我已经看到编码标准,指定参数列表应该格式化多长时间,重构是不可能的。 一个这样的例子是每行使用双缩进和一个参数(不适用于所有函数 – 仅适用于具有多行参数的函数)。
例如
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);
这里的重点是创build一个一致的布局,并查找大量参数的所有function。
这里有点晚,但是由于没有人做过,所以我想指出这个问题的一个明显的方面:对我来说,一个需要这么多参数的函数可能会做很多计算,所以考虑一下这个可能性作为第一步分解它在较小的function。
这应该可以帮助您构build数据。