正如你所說,必須小心使用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)
[從鎬]問題使用這種技術[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
但有些厭惡類變量之多全局變量。
Rails有一個叫做'alias_method_chain'的方法,可以幫助解決這個問題。 –
不錯。迂腐的問題:如果在混合進班級的模塊中完成了猴子補丁嗎? – Nathan
@Nathan:我不認爲「猴子修補」有一個確切的定義。在Ruby中,存在元編程,這只是正常的做事方式(比如在C++中使用模板),然後是極端的元編程形式,它們變得聰明或積極,並以不明確的方式修改已經定義的事物。如果你太聰明,那可能是壞的補丁。如果你只是想解決問題,就像你的情況一樣,這可能是可以接受的猴子補丁。例如,Rails經常使用'alias_method_chain'。 –