传递散列而不是方法参数

我发现在Ruby(通常是dynamictypes语言)中,一个非常常见的做法是传递一个散列,而不是声明具体的方法参数。 例如,不是用参数声明一个方法,而是像这样调用它:

def my_method(width, height, show_border) my_method(400, 50, false) 

你可以这样做:

 def my_method(options) my_method({"width" => 400, "height" => 50, "show_border" => false}) 

我想知道你的意见。 这是一种好还是坏的做法,我们应该做还是不做? 在什么情况下使用这种做法是有效的,在什么情况下会是危险的?

这两种方法都有各自的优点和缺点,当使用选项散列代替标准参数时,您会在定义方法的代码中失去清晰度,但是由于使用选项散列创build的伪命名参数,因此每当使用该方法时都会清晰起来。

我的一般规则是如果你有一个方法(超过3或4)或大量的可选参数很多参数,然后使用选项哈希,否则使用标准参数。 然而,当使用选项散列时,始终在说明可能参数的方法定义中包含注释非常重要。

Ruby有隐含的散列参数,所以你也可以写

 def my_method(options = {}) my_method(:width => 400, :height => 50, :show_border => false) 

和Ruby 1.9和新的哈希语法可以

 my_method( width: 400, height: 50, show_border: false ) 

当一个函数需要超过3-4个参数时,更容易看出哪个是什么,而不计算相应的位置。

我会说,如果你是:

  1. 有超过6个方法参数
  2. 通过一些必需的选项 ,一些可选的和一些默认值

你很可能想要使用散列。 查看文档中的参数意味着更容易。

对于那些认为很难确定某个方法需要什么选项的人来说,这只是意味着代码logging不完整。 使用YARD ,您可以使用@option标签来指定选项:

 ## # Create a box. # # @param [Hash] options The options hash. # @option options [Numeric] :width The width of the box. # @option options [Numeric] :height The height of the box. # @option options [Boolean] :show_border (false) Whether to show a # border or not. def create_box(options={}) options[:show_border] ||= false end 

但在这个具体的例子中,有这么几个简单的参数,所以我认为我会这样做:

 ## # Create a box. # # @param [Numeric] width The width of the box. # @param [Numeric] height The height of the box. # @param [Boolean] show_border Whether to show a border or not. def create_box(width, height, show_border=false) end 

我认为这种parameter passing的方法在有多个参数的情况下或者有多个可选参数的时候会更清晰。 它本质上使方法调用明显自我logging。

Ruby中使用散列而不是forms参数是不常见的做法。

我认为这是混淆了传递散列作为参数的常见模式,当参数可以采取一些值,例如在GUI工具包中设置窗口的属性。

如果您的方法或函数有多个参数,则明确声明并传递它们。 你会得到这样的好处:口译员会检查你是否通过了所有的论点。

不要滥用语言function,知道何时使用它,何时不使用它。

使用Hash作为参数的好处是,您可以删除参数数量和顺序的依赖关系。

实际上,这意味着您稍后可以灵活地重构/更改您的方法,而不会破坏与客户端代码的兼容性(因为您无法实际更改客户端代码,所以在构build库时非常有用)。

(如果您对Ruby中的软件devise感兴趣,Sandy Metz的“Ruby中实用的面向对象的devise”是一本很棒的书)

这是一个很好的做法。 您不需要考虑方法签名和参数的顺序。 另一个好处是,你可以轻松地省略你不想input的参数。 你可以看看ExtJS框架,因为它正在使用这种广泛传递的参数types。

这是一个权衡。 你失去了一些清晰度(我怎么知道什么params通过)和检查(我通过正确数量的参数?),并获得灵活性(该方法可以默认参数它没有收到,我们可以部署一个新版本更多的参数和打破现有的代码)

你可以看到这个问题是更大的强/弱types讨论的一部分。 请参阅Steve Yegge的博客。 我曾经在C和C ++中使用过这种风格,希望支持相当灵活的parameter passing。 可以说是一个标准的HTTP GET,带有一些查询参数正是这种风格。

如果你喜欢哈希appraoch我想说,你需要确保你的testing是非常好的,不正确的拼写参数名称的问题只会在运行时显示。

我确定没有人使用dynamic语言,但是当你开始将哈希函数传递给函数时,想想你的程序将会受到的性能损失。

解释器可能足够聪明,可以创build一个静态const哈希对象,并且只能通过指针来引用它,如果代码使用的是所有成员为源代码文本的哈希。

但是,如果这些成员中的任何一个都是variables,那么每次调用hash时都必须重构它。

我已经做了一些Perl优化,这种事情可以在内部代码循环中变得明显。

函数参数performance更好。

一般来说,我们应该总是使用标准的论点,除非这是不可能的。 当你不必使用它们时使用选项是不好的做法。 标准的论点是清楚的,自我logging(如果正确命名)。

一个(也许是唯一的)使用选项的原因是如果函数收到的参数不处理,而只是传递给另一个函数。

这是一个例子,说明:

 def myfactory(otype, *args) if otype == "obj1" myobj1(*args) elsif otype == "obj2" myobj2(*args) else puts("unknown object") end end def myobj1(arg11) puts("this is myobj1 #{arg11}") end def myobj2(arg21, arg22) puts("this is myobj2 #{arg21} #{arg22}") end 

在这种情况下,“myfactory”甚至不知道“myobj1”或“myobj2”所需的参数。 'myfactory'只是将parameter passing给'myobj1'和'myobj2',他们有责任检查和处理它们。

哈希对传递多个可选参数特别有用。 我使用散列,例如初始化一个参数是可选的类。

 class Example def initialize(args = {}) @code code = args[:code] # No error but you have no control of the variable initialization. the default value is supplied by Hash @code = args.fetch(:code) # returns IndexError exception if the argument wasn't passed. And the program stops # Handling the execption begin @code = args.fetch(:code) rescue @code = 0 end end