【发布时间】:2014-10-07 05:37:28
【问题描述】:
例如,array.pop 不需要 bang 来永久更改数组。为什么会这样?开发这些特定的 Ruby 方法背后的原因是什么?
【问题讨论】:
-
一般情况下是 ruby 还是 Rails 或 Rails 的某些子集,如 ActiveRecord?
-
这个约定通常在 Ruby 社区中使用。
例如,array.pop 不需要 bang 来永久更改数组。为什么会这样?开发这些特定的 Ruby 方法背后的原因是什么?
【问题讨论】:
Bang 方法最常用于区分同一方法的危险版本和安全版本。以下是一些可能需要区分 bang/no-bang 组合的示例:
但是,如果只有一个版本是有意义的,则惯例是不使用。例如,poping 一个数组而不实际更改它是没有意义的。在这种情况下,它最终会是一个不同的操作:Array#last。许多方法会更改它们被调用的对象,例如 setter。我们也不需要一气呵成地写这些,因为很明显它们会改变对象。
最后,有一些例外情况,一些开发人员可能会使用 bang 方法而没有实现无 bang 对应项。在这些情况下,bang 只是用作使方法调用在视觉上突出的一种方式。例如:
【讨论】:
! 的约定,以表示“做一些你可能没想到的事情”——因此,旨在通过有条件地引发异常或无参数持久性变体来控制流程的方法(例如model_object.activate!)。但是,这些有时是单个开发团队对约定的想法,而不是 Ruby 范围内的。
pop 是准确的,但! 后缀并不意味着某些东西是突变体,而是意味着它很危险。请参阅我的回答和我的ActiveRecord::Base#save! 示例。
bang 用于区分同一方法的危险版本和不太危险的版本。 pop方法只有一个,所以没什么好区分的。
注意:方法的名称与它的作用完全无关。一个方法是否具有破坏性取决于它执行的代码,而不是它的名称。
【讨论】:
bang 在 Ruby 中的意思是“小心”。这意味着您应该谨慎使用该方法,仅此而已。我再也找不到参考资料了,但权威人士明确表示 bang ≠ 破坏性方法。 Bang 只是一个与谨慎相关的语义元素。程序员可以权衡一切并决定何时使用 bang。
例如,在我的模拟 gem 中,我使用#step 方法来获取步长。
simulation.step #=> 0.42
和step! 方法来实际执行模拟步骤。
simulation.step! #=> takes the simulation to the next time step
但是对于#reset的方法,我觉得“reset”这个词已经够啰嗦了,没必要用bang来警告用户模拟状态会被破坏:
simulation.reset #=> resets the simulation back to the initial state
P.S.:现在我记得,曾几何时,Matz 半开玩笑地说,他后悔将带有 bang 的方法引入 Ruby,因为 bang 在语义上是如此模棱两可。
【讨论】:
! 的后缀意味着一个方法是另一个方法的危险版本。例如,save! 是 save 的危险版本。危险可能意味着在适当的位置进行编辑、执行更严格的错误等。在危险的方法上不需要使用! 后缀,但不需要更安全的对应方法。此外,这只是一个命名约定,因此 Ruby 不会限制方法是否以 ! 结尾时可以做什么和不可以做什么。
有一个普遍的误解,即每个编辑就地内容的方法都应该以! 结尾。这不是真的,! 仅在已经存在的方法的更危险版本时才需要,这并不一定意味着危险方法编辑到位。例如,在 Rails 中,ActiveRecord::Base#save! 是执行验证的ActiveRecord::Base#save 的一个版本。
【讨论】:
Array#pop!?
Array#pop! 方法。
Array#pop 已经很危险,因为它可以就地编辑,不需要危险版本。
pop! 方法确实存在......我会使用除了pop right there之外的一个例子