我已浏览并没有找到以下答案:
您将使用别名方法吗?
class Vampire
attr_reader :name, :thirsty
alias_method :thirsty?, :thirsty
end
我会使用的唯一原因是能够使用我定义的任何方法使用问号吗?我相信您不能在实例变量中使用问号。
答案 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核心库中的一些示例是收集方法,这些收集方法的名称对于使用map
,reduce
等功能性编程语言的人,以及使用Smalltalk系列编程语言的人来说,例如collect
,inject
,select
和标准英语名称,例如find_all
。)另一种可能性是您构建一个 Fluent接口并希望它能更流畅地阅读,就像这样:
play this
and that
and something_else
在这种情况下,and
可能是play
的别名。
另一个相关的原因(我们将其称为原因1.5)是您要实现某种协议,并且已经有一种方法可以正确实现该协议的语义,但是名称错误。让我们假设一个具有两个方法map_seq
和map_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#prepend
。 prepend
的优点在于它只是“继承”,我们可以简单地使用super
:
module Shouter
def some_method
super.upcase
end
end
class SomeClass
prepend Shouter
end
例如, Module#prepend
是Module#alias_method_chain
在ActiveSupport 5.0中过时并在5.1中被删除的原因。不再需要所有这些扭曲。
因此,总结一下:使用alias_method
的主要原因有两个:从字面上做一个别名,即,同一操作的两个名称,以及为方法包装创建备份副本。第二个不再有效,也许可以说从来没有。今天,只有第一个原因是使用alias_method
的有效理由。