Ruby on Rails:alias_method_chain,它到底是做什么的?

59
4个回答

118

1 - 它还在被使用吗?

显然是的,alias_method_chain() 在 Rails 中 仍然被使用(截至版本 3.0.0)。

2 - 何时使用alias_method_chain以及为什么使用它?

注意:以下内容主要基于 Paolo Perrotta 的《Ruby元编程》中关于alias_method_chain()的讨论,这是一本非常好的书籍,你应该去阅读一下。)

让我们从一个基本示例开始:

class Klass
  def salute
    puts "Aloha!"
  end
end

Klass.new.salute # => Aloha!

现在假设我们想要在Klass#salute()周围添加日志记录行为。我们可以使用Perrotta所称的around alias来实现:
class Klass
  def salute_with_log
    puts "Calling method..."
    salute_without_log
    puts "...Method called"
  end

  alias_method :salute_without_log, :salute
  alias_method :salute, :salute_with_log
end

Klass.new.salute
# Prints the following:
# Calling method...
# Aloha!
# ...Method called

我们定义了一个名为salute_with_log()的新方法,并将其重命名为salute()。以前调用salute()的代码仍然有效,但它也获得了新的日志记录行为。我们还定义了一个原始salute()的别名,因此我们仍然可以进行无需记录日志的问候:
Klass.new.salute_without_log # => Aloha!

因此,salute()现在被称为salute_without_log()。如果我们想要记录日志,可以调用salute_with_log()salute(),它们是同一方法的别名。感到困惑?很好!

根据Perrotta的说法,这种around alias在Rails中非常常见:

看另一个Rails解决问题的例子。几个版本之前,Rails代码包含许多相同的习惯用语:使用Around Alias(155)添加功能到方法中,并将旧版本的方法重命名为类似于method_without_feature()的名称。除了每次更改的方法名称之外,执行此操作的代码始终相同,复制到各个地方。在大多数语言中,您无法避免这种重复。在Ruby中,您可以在模式上撒些元编程魔法,并将其提取到自己的方法中...于是诞生了alias_method_chain()

换句话说,您提供原始方法foo()和增强方法foo_with_feature(),最终得到三种方法:foo()foo_with_feature()foo_without_feature()。前两个包含该特性,而第三个不包含该特性。为了避免重复使用这些别名,ActiveSupport提供的alias_method_chain()会为您处理所有别名。

20
小小的补充:现在我们只需键入alias_method_chain :foo, :feature,我们就会得到3个方法:foo, foo_with_feature, foo_without_feature,正如Yases Sulaiman之前所描述的那样,这些方法已经被正确地命名别名了。 - freemanoid
3
“任何足够先进的技术都无法与魔法区分开来。” 阿瑟·克拉克 - Joshua Pinter

16

5

我不确定它是否在Rails 3中已经过时,但在之前的版本中仍然活跃使用。

您可以使用它在调用方法的任何地方都不修改的情况下,在方法被调用之前(或之后)注入一些功能。请参阅以下示例:

module SwitchableSmtp
  module InstanceMethods
    def deliver_with_switchable_smtp!(mail = @mail)
      unless logger.nil?
        logger.info  "Switching SMTP server to: #{custom_smtp.inspect}" 
      end
      ActionMailer::Base.smtp_settings = custom_smtp unless custom_smtp.nil?
      deliver_without_switchable_smtp!(mail = @mail)
    end
  end
  def self.included(receiver)
    receiver.send :include, InstanceMethods
    receiver.class_eval do
      alias_method_chain :deliver!, :switchable_smtp
    end
  end
end

这是针对ActionMailer的新增功能,允许在每次调用deliver!时交换SMTP设置。通过调用alias_method_chain,您可以定义一个名为deliver_with_switchable_smtp!的方法,在其中执行自定义操作,并在完成后从中调用deliver_without_switchable_smtp!alias_method_chain将旧的deliver!别名为您的新自定义方法,因此您的应用程序的其余部分甚至不知道deliver!现在也执行您的自定义操作。

alias_method_chain :deliver!, :switchable_smtp 应该改为 alias_method_chain :deliver!, :deliver_with_switchable_smtp!,对吗? - Waseem

3

它有用吗?

似乎是这样的。这是Rails开发人员常见的做法。

何时使用alias_method_chain以及为什么使用它?

尽管存在警告,alias_method_chain仍然是向现有方法注入功能的主要策略,至少在Rails 2.x中是如此,并且被许多扩展人员所遵循。Yehuda应该从Rails 3.0中删除alias_method_chain,从他在Rails票务中的帖子和评论来看。它仍然被许多扩展使用,这些扩展在执行的某些点上添加自定义行为,例如日志记录器、错误报告、基准测试、数据注入等。

在我看来,最好的替代方案是包含一个模块,这样你就可以进行装饰而不是委托。(例如,遵循这篇文章中的第4个示例)。这样,即使您想要单独更改对象,也不会污染类的方法。这种方法的缺点是每注入一个模块就会增加一次方法查找链,但这正是模块的用途。

非常有趣的问题,我会关注其他人对此的看法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接