首页 > 解决方案 > 避免在Ruby中改变方法参数的好习惯?

问题描述

我知道避免在 Ruby 中改变方法参数是一种很好的做法(除非这是特别的意图)。

我应该如何管理它的简单编码经验法则(在风格、命名约定等方面)是什么?

例如,这是一种好的做法吗?

def array_of_three(a, b, c)
   d = a.dup
   e = b.dup
   f = c.dup
   # Do work on d, e and f
   return [d, e, f]
end

我应该如何命名方法参数与函数内可以变异的重复项?(假设上述方法是“正确的”!)

谢谢。

标签: ruby

解决方案


Ruby 的“良好实践”通常归结为不改变您不拥有的数据。问题是所有权可能有些模糊,因此如果有疑问,您会想假设您拥有数据并在需要对其进行变异时制作副本。

不良行为的表现方式如下所示:

a = [ 1, 2, 3 ]

do_stuff(a) # Works
do_stuff(a) # Doesn't do anything for some reason

a
# => [ ]    # Hey! Who did that?

让我们说do_stuff是一个混蛋并这样做:

def do_stuff(a)
  while (v = a.pop)
    puts a
  end
end

这会破坏你给出的论点,这很糟糕。相反,您应该采用非破坏性方法:

def do_stuff(a)
  a.each do |v|
    puts v
  end
end

或者,如果需要操作该结构,则制作一个副本,例如:

def do_stuff(a)
  a = a.uniq.sort
  while (v = a.pop)
    puts v
  end
end

测试您的方法是否正常运行的一个好方法是在测试中为它们提供冻结数据:

a = [ 1, 2, 3 ].freeze

do_stuff(a) # Fails trying to manipulate frozen object

我倾向于使用积极冻结的数据(例如deep_freeze)编写单元测试,以确保无法修改参数。这很快就解决了所有权问题。

如果您的函数具有这样的方法签名:

def do_stuff(*a, **b)
  # ...
end

然后,您将同时拥有a(varargs) 和b(kwargs),因为它们是专门针对您的方法而划分的。在所有其他情况下,您需要小心。这就是合约编程很重要的地方,因为您的方法的调用者必须知道哪些参数已移交,哪些未移交。文件必须清楚地说明这一点。


推荐阅读