正如你所说, alias_method 必须小心使用。鉴于这个人为的例子:
module CustomClient
...
host.class_eval do
alias :old_account_balance :account_balance
def account_balance ...
old_account_balance
end
...
class CoreClass
def old_account_balance ... defined here or in a superclass or
in another included module
def account_balance
# some new stuff ...
old_account_balance # some old stuff ...
end
include CustomClient
end
你最终会出现一个无限循环,因为在别名之后,old_account_balance 是 account_balance 的副本,它现在调用自己:
$ ruby -w t4.rb
t4.rb:21: warning: method redefined; discarding old old_account_balance
t4.rb:2: warning: previous definition of old_account_balance was here
[ output of puts removed ]
t4.rb:6: stack level too deep (SystemStackError)
[来自 Pickaxe] 这种技术 [alias_method] 的问题在于,您依赖的不是名为 old_xxx 的现有方法。一个更好的选择是使用实际上是匿名的方法对象。
话虽如此,如果您拥有源代码,一个简单的别名就足够了。但对于更一般的情况,我将使用 Jörg 的方法包装技术。
class CoreClass
def account_balance
puts 'CoreClass#account_balance, stuff deferred to the original method.'
end
end
module CustomClient
def self.included host
@is_defined_account_balance = host.new.respond_to? :account_balance
puts "is_defined_account_balance=#{@is_defined_account_balance}"
# pass this flag from CustomClient to host :
host.instance_variable_set(:@is_defined_account_balance,
@is_defined_account_balance)
host.class_eval do
old_account_balance = instance_method(:account_balance) if
@is_defined_account_balance
define_method(:account_balance) do |*args|
puts 'CustomClient#account_balance, additional stuff'
# like super :
old_account_balance.bind(self).call(*args) if
self.class.instance_variable_get(:@is_defined_account_balance)
end
end
end
end
class CoreClass
include CustomClient
end
print 'CoreClass.new.account_balance : '
CoreClass.new.account_balance
输出 :
$ ruby -w t5.rb
is_defined_account_balance=true
CoreClass.new.account_balance : CustomClient#account_balance, additional stuff
CoreClass#account_balance, stuff deferred to the original method.
为什么不是类变量 @@is_defined_account_balance ?[来自 Pickaxe] 包含 include 的模块或类定义可以访问它所包含的模块的常量、类变量和实例方法。
它将避免将其从 CustomClient 传递到主机并简化测试:
old_account_balance if @@is_defined_account_balance # = super
但是有些人不喜欢类变量和全局变量一样。