传递哈希值而不是方法参数

时间:2009-12-18 09:54:28

标签: ruby oop coding-style

我在Ruby中看到(通常是动态类型语言),一种非常常见的做法是传递哈希,而不是声明具体的方法参数。例如,不是使用参数声明一个方法,而是像这样调用它:

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

我想知道您对此的看法。这是好事还是坏事,我们应该这样做吗?在什么情况下使用这种做法是有效的,哪种情况可能是危险的?

11 个答案:

答案 0 :(得分:40)

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 :(得分:27)

这两种方法都有各自的优点和缺点,当您使用选项哈希替换标准参数时,您在定义方法的代码中会失去清晰度,但是在使用该方法时会因为使用选项创建的伪命名参数而获得清晰度散列。

我的一般规则是,如果你有一个方法(超过3或4个)或许多可选参数的大量参数,那么使用选项哈希,否则使用标准参数。但是,在使用选项哈希时,始终在方法定义中包含描述可能参数的注释非常重要。

答案 2 :(得分:7)

我会说,如果你是:

  1. 拥有超过6个方法参数
  2. 传递选项,其中包含一些必需项,一些是可选项,另一些是默认值
  3. 你很可能想要使用哈希。如果不在文档中查找,就可以更容易地看到参数的含义。

    对于那些说你很难确定方法采用什么选项的人来说,这只是意味着代码记录很差。使用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
    

答案 3 :(得分:3)

我认为当有多个参数或有许多可选参数时,这种参数传递方法会更加清晰。它实质上使方法调用明显地自我记录。

答案 4 :(得分:3)

在Ruby中使用哈希而不是形式参数并不常见。

我认为这是混淆与将参数可以采用多个值的散列作为参数传递的常见模式。在GUI工具包中设置Window的属性。

如果您的方法或函数有许多参数,则显式声明它们并传递它们。您将获得解释器将检查您是否已通过所有参数的好处。

不要滥用语言功能,知道何时使用它以及何时不使用它。

答案 5 :(得分:3)

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

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

(如果您对Ruby中的软件设计感兴趣,Sandy Metz' s "Practical Object-Oriented Design in Ruby"是一本很棒的书)

答案 6 :(得分:2)

这是一个很好的做法。您不需要考虑方法签名和参数的顺序。另一个优点是您可以轻松省略您不想输入的参数。 您可以查看ExtJS框架,因为它正在广泛使用这种类型的参数。

答案 7 :(得分:1)

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

您可以将此问题视为较大的强/弱类型讨论的一部分。请在此处查看Steve yegge's博客。在我想要支持非常灵活的参数传递的情况下,我在C和C ++中使用了这种风格。可以说是一个标准的HTTP GET,带有一些查询参数就是这种风格。

如果你选择哈希appraoch我会说你需要确保你的测试真的很好,错误拼写的参数名称的问题只会在运行时出现。

答案 8 :(得分:1)

我确信没有人会使用动态语言,但是当你开始向函数传递哈希值时,请考虑一下你的程序会受到的性能损失。

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

但是如果这些成员中的任何一个是变量,则每次调用时都必须重建哈希值。

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

功能参数表现更好。

答案 9 :(得分:0)

一般情况下,我们应该始终使用标准参数,除非它不可能。在不必使用选项时使用选项是不好的做法。标准参数清晰且自我记录(如果正确命名)。

使用选项的一个(也许是唯一的)原因是if函数接收的参数不会处理但只是传递给另一个函数。

这是一个例子,说明了:

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'只是将参数传递给'myobj1'和'myobj2',他们有责任检查和处理它们。

答案 10 :(得分:0)

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

实施例

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