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

时间:2019-06-09 01:06:52

标签: ruby alias-method

我已浏览并没有找到以下答案:

您将使用别名方法吗?

class Vampire
 attr_reader :name, :thirsty

 alias_method :thirsty?, :thirsty
end

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

2 个答案:

答案 0 :(得分:3)

我认为这是从我回答的较早的问题开始的,我在此问题中建议使用b,因此我对此有一些额外的说明,以解释其在该上下文中的使用。

在您的代码段中,您有一些读取a的代码,该代码基本上是同名实例变量(alias_method)的吸气剂

attr_reader :thirsty

在原始代码段中,您有一个断言:

@thirsty

您还拥有简单地为def thirsty @thirsty end 方法返回refute vampire.thirsty? 的代码,但断言失败。

至少有两种方法可以修改代码,以使对true的调用有效并通过您的断言:

创建一个调用thirsty?阅读器或访问thirsty?实例变量本身的方法:

thirsty

另一种方法是使用@thirsty,其功能与上述等效。它将def thirsty? thirsty # or @thirsty end 别名为alias_method的{​​{1}}别名,thirsty?thirsty实例变量中读取

Reference是我给的另一个答案

您最好根本不使用attr_reader,而只是按照Sergio在他的评论中指出的那样做:

attr_reader

答案 1 :(得分:3)

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

第一个原因是您只想拥有两个名称完全相同的方法,而这两个方法的作用完全相同。这样做的一个原因可能是同一动作有两个同样广泛使用的术语,并且您想使人们更容易编写其社区可以理解的代码。 (Ruby核心库中的一些示例是收集方法,这些收集方法的名称对于使用mapreduce等功能性编程语言的人,以及使用Smalltalk系列编程语言的人来说,例如collectinjectselect和标准英语名称,例如find_all。)另一种可能性是您构建一个 Fluent接口并希望它能更流畅地阅读,就像这样:

play this
and that
and something_else

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

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

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

过去使用alias_method的第二个主要原因与它的语义上的一个重要细节有关:当您覆盖或使用猴子补丁两种方法中的任何一个时,这只会影响 这个名字,但是另一个过去,这种方法用于包装方法的行为,例如:

class SomeClass
  def some_method
    "Hello World"
  end
end

这有点无聊。我们希望我们的方法大声疾呼!但是,我们不想只复制并重新实现该方法,我们想重新使用它的内部实现,而不必知道它是如何在内部实现的。而且我们想猴子修补它,以便该方法的所有客户端都具有喊叫行为。流行的方法是这样的:

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

在此示例中,我们使用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

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

但是,从Ruby 2.0开始,有一种更好的方法包装方法:Module#prependprepend的优点在于它只是“继承”,我们可以简单地使用super

module Shouter
  def some_method
    super.upcase
  end
end

class SomeClass
  prepend Shouter
end
例如,

Module#prependModule#alias_method_chain在ActiveSupport 5.0中过时并在5.1中被删除的原因。不再需要所有这些扭曲。

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