我什么时候应该使用别名方法?- 红宝石

mph*_*h85 1 ruby alias-method

我浏览了一下,没有看到答案:

您会使用别名方法做什么?

class Vampire
 attr_reader :name, :thirsty

 alias_method :thirsty?, :thirsty
end
Run Code Online (Sandbox Code Playgroud)

我使用问号的唯一原因是能够在我定义的任何方法中使用问号吗?我相信您不能对实例变量使用问号。

Jör*_*tag 5

使用 的原因有两个Module#alias_method,一个是当前且有效的,另一个是过时的并且实际上从来没有必要。

第一个原因是您只是希望有两个具有不同名称的方法来执行完全相同的操作。原因之一可能是同一操作有两个同样广泛使用的术语,并且您希望让人们更容易编写社区可以理解的代码。(Ruby 核心库中的一些示例是集合方法,这些方法的名称对于来自函数式编程语言的人来说很熟悉,例如mapreduce,来自 Smalltalk 编程语言系列的人,例如collectinjectselect和 标准英文名称,例如find_all。)另一种可能性是您构建一个流畅的界面并希望它阅读起来更流畅,如下所示:

play this
and that
and something_else
Run Code Online (Sandbox Code Playgroud)

在这种情况下,and可能是 的别名play

另一个相关原因(我们称之为原因 1.5)是您想要实现某个协议,并且您已经有一个可以正确实现该协议语义的方法,但它的名称错误。让我们假设一个假设的集合框架,它有两个方法map_seqmap_nonseq。第一个执行map并保证副作用的顺序,而第二个不保证副作用的顺序,甚至可以异步、并发或并行地执行映射操作。这两种方法实际上具有不同的语义,但如果您的数据结构不适合并行化,那么您可以简单地实现map_seq并创建map_nonseq别名。

在这种情况下,驱动程序并不是要为同一操作提供两个名称,而是要为两个名称提供相同的实现(如果这句话有意义的话:-D)。

过去使用的第二个主要原因与其alias_method语义的一个重要细节有关:当您重写或猴子修补这两种方法中的任何一个时,这只会影响名称,而不会影响另一个。这在过去用于包装方法的行为,如下所示:

class SomeClass
  def some_method
    "Hello World"
  end
end
Run Code Online (Sandbox Code Playgroud)

这有点无聊。我们希望我们的方法能够SHOUT!但是,我们不想只是复制和重新实现该方法,我们希望重用其内部实现,而不必知道它的内部实现方式。我们想要对其进行猴子修补,以便该方法的所有客户端都有喊叫行为。流行的方法是这样的:

class SomeClass
  alias_method :__some_method_without_shouting :some_method

  def __some_method_with_shouting
    __some_method_without_shouting.upcase
  end

  alias_method :some_method :__some_method_with_shouting
end
Run Code Online (Sandbox Code Playgroud)

在此示例中,我们用于alias_method创建我们正在猴子修补的方法的“备份”,以便我们可以从该方法的猴子修补版本中调用它。(否则,该方法就会消失。)这实际上是alias_method.

这个习惯用法是如此流行和广泛使用,以至于一些库甚至提供了它的实现,例如 ActiveSupport 的Module#alias_method_chain.

但请注意,这个习语有一些不太好的特性。一是我们正在用所有这些_with__without_方法污染命名空间。例如,当您查看对象的方法时,您将看到所有这些。另一个问题是人们仍然可以直接调用旧方法,但大概我们有理由修补它,因为我们不想要旧行为。(否则,我们可以创建一个使用新名称的方法来调用旧方法,例如shout。)

一直有一个更好的替代方案,但没有被广泛使用:

class SomeClass
  some_method_without_shouting = instance_method(:some_method)

  define_method(:some_method) do
    some_method_without_shouting.bind(self).().upcase
  end
end
Run Code Online (Sandbox Code Playgroud)

在这里,我们将旧方法存储在局部变量中,并使用块来定义新方法(通过Module#define_method)。局部变量在类主体末尾超出范围,因此永远无法再在任何地方访问它。但是块是闭包,它们关闭周围的词法环境,因此传递给的块define_method(并且只有这个块)仍然可以访问变量绑定。这样,旧的实现就完全隐藏了,并且不存在命名空间污染。

然而,从 Ruby 2.0 开始,对于这种方法包装有一个更好的解决方案:Module#prepend。的美妙之prepend处在于它“只是继承”,我们可以简单地使用super

module Shouter
  def some_method
    super.upcase
  end
end

class SomeClass
  prepend Shouter
end
Run Code Online (Sandbox Code Playgroud)

Module#prependModule#alias_method_chain例如,这就是 ActiveSupport 5.0 中弃用并在 5.1 中删除的原因。所有这些扭曲都不再必要了。

因此,总结一下:使用它有两个主要原因alias_method:从字面上创建别名,即同一操作的两个名称,并为方法包装创建备份副本。第二个不再有效,也许可以说从来都不是。今天,只有第一个原因才是使用的正当理由alias_method