传递哈希而不是方法参数[关闭]

Posted

技术标签:

【中文标题】传递哈希而不是方法参数[关闭]【英文标题】:Passing hashes instead of method parameters [closed] 【发布时间】:2010-12-27 23:46:32 【问题描述】:

我看到在 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)

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

【问题讨论】:

一点注意:width => 400, height => 50, show_border => false 不是有效的 ruby​​ 语法。我想你的意思是 :width => 400, :height => 50, :show_border => falsewidth: 400, height: 50, show_border: false (后者仅在 ruby​​ 1.9.1 中有效) 【参考方案1】:

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

示例

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

【讨论】:

【参考方案2】:

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

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

这是一个例子,说明:

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”,它们有责任检查和处理它们。

【讨论】:

【参考方案3】:

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

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

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

【讨论】:

虽然您的说法是正确的,但我认为这不是保持库兼容的最佳方式。您可以通过简单地添加新参数作为可选参数来实现相同的目的。它们不会破坏兼容性,并且仍然具有清晰性和自我记录的优势。 @DraganNikolic 我也支持 sany metz 的东西 - 不必安排手动参数列表的好处是巨大的,而且书中还列出了其他自我记录代码的方法 许多人(包括我!)经常忘记的另一件事是参数的顺序也是依赖关系!这意味着只使用可选参数意味着您不能出于任何原因更改参数的顺序(但通常您不想要很多 args 参数!)【参考方案4】:

我敢肯定没有人会在意使用动态语言,但请考虑一下当您开始将哈希值传递给函数时您的程序将受到的性能损失。

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

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

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

函数参数的表现要好得多。

【讨论】:

【参考方案5】:

我会说,如果你是:

    方法参数超过 6 个 传递选项,这些选项有一些是必需的,一些是可选的,还有一些是默认值的

您很可能想要使用哈希。无需查看文档即可更轻松地了解参数的含义。

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

【讨论】:

【参考方案6】:

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

我的一般规则是,如果您的方法有很多参数(超过 3 个或 4 个)或有很多可选参数,则使用选项散列,否则使用标准参数。但是,在使用选项哈希时,务必在方法定义中包含描述可能参数的注释。

【讨论】:

现在,如果我有一个选项散列,我可以将整个散列放入,还是必须单独传递键 => 值? 确实各有利弊,但我不同意一般规则。我认为选项通常是不好的做法,只有在这是解决问题的唯一方法时才应该使用。【参考方案7】:

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

我认为 这与将哈希作为参数传递的常见模式混淆了,当参数可以采用多个值时,例如在 GUI 工具包中设置窗口的属性。

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

不要滥用语言特性,知道什么时候用,什么时候不用。

【讨论】:

如果您需要可变数量的参数,只需使用def method(*args),哈希并不能解决该特定问题 感谢您指出潜在的混淆。我真的在考虑原始海报提出的情况,其中参数的顺序和数量是可变的。 补充一点,该反模式也称为Magic Container【参考方案8】:

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 个参数时,更容易看出哪个是什么,而不用计算各自的位置。

【讨论】:

现在从 Ruby 2.7 开始,implicit 散列参数已被弃用,并将在 Ruby 3.0 中删除——这意味着,我们再次需要 显式 传递哈希 (see)。此处显示的 Ruby 1.9 语法仍然可以调用带有 keyword 参数的方法。【参考方案9】:

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

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

如果您采用哈希方法,我会说您需要确保您的测试非常好,参数名称拼写错误的问题只会在运行时出现。

【讨论】:

【参考方案10】:

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

【讨论】:

【参考方案11】:

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

【讨论】:

以上是关于传递哈希而不是方法参数[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

将参数中的字段传递给外部方法调用是不是违反了打开/关闭原则?

将 Rails 参数哈希传递给命名路由的更有效方法

哈希可枚举方法:仅传递一个参数时的行为不一致

如何在 node.js 中要求一个文件并在请求方法中传递一个参数,而不是传递给模块?

值传递和引用传递(不是引用类型的传递)的区别

Java中的参数传递