轉自:http://blog.csdn.net/feigeswjtu/article/details/51542061ruby
ruby中的方法是一個很神奇的概念,怎麼個神奇法呢,聽我慢慢道來。性能
在介紹ruby的方法以前,咱們先說下什麼是靜態語言,在編譯階段,編譯器都會檢查方法調用的對象是否有一個這樣的方法,若是沒有就直接報錯,這種稱爲靜態類型檢查,這種語言稱爲靜態語言。衆所周知,ruby是動態語言,只有真正調用這個方法的時候,找不到這個方法纔會報錯錯誤,方法查找過程能夠見個人另外一篇文章(ruby之方法查找)。ui
究竟是哪兒種類型的語言好呢,各有利弊吧,也不能說哪兒個語言好,只能說哪兒個語言適合,好比,通常的動態語言,開發效率很高,可是會有必定的性能損失,靜態語言開發效率低,常常要寫不少相似set/get方法,可是若是有錯誤,在編譯階段就能發現,不用在運行中發現錯誤。在這方面,語言選擇我的的理解是初期,須要很快的迭代時能夠用動態語言,可是等到穩定後,仍是要回歸到靜態語言。spa
閒話很少說了,這篇文章是用來解釋ruby中的動態方法的。.net
ruby中的動態方法只要跟send, define_method, method_missing這幾個方法相關。code
send
- class SendClass
- def get_one_name
- 'one_name'
- end
-
- def get_two_name
- 'two_name'
- end
-
- def get_three_name
- 'three_name'
- end
- end
-
- s = SendClass.new
-
- puts s.send(:get_one_name)
- puts s.send(:get_two_name)
- puts s.send(:get_three_name)
- puts s.send(:get_four_name)
send方法至少要有一個參數,這個參數就是要調用的方法名,若是找不到這個方法,就會報undefined method的錯誤,send方法用處也是比較多的,若是有一個對象有不少屬性,可是調用這些屬性方法時,若是不知道要調用的是哪一個屬性,就能夠用這個方法。
可是send調用的這些方法仍是必須存在的呀,並非一些動態的方法,只是動態調用,那麼有沒有可能動態定義方法呢?別急,這就是咱們要介紹的define_method方法。
define_method
- class SendClass
-
-
-
-
-
-
-
-
-
-
-
- [:one_name, :two_name, :three_name].each do |name|
- define_method("get_#{name}"){
- name
- }
- end
- end
-
- s = SendClass.new
-
- puts s.send(:get_one_name)
- puts s.send(:get_two_name)
- puts s.send(:get_three_name)
- puts s.send(:get_four_name)
看吧,達到了一樣的效果,只用了一條語句就定義了幾個方法,那麼還有沒有其餘方法能達到這樣的效果呢?答案是確定的,那就是method_missing。
method_missing
method_missing是一個魔鬼方法,使用不當的狀況下,會發生不少意想不到的問題。不過它仍是頗有用的,在介紹它以前,咱們先會議一下方法的查找,遵循右上法則,直到找到BasicObject中。那麼咱們看看BasicObject有沒有這個方法。
- 1.9.3-p551 :016 > BasicObject.method(:method_missing)
- =>
找到了,method_missing方法是定義在BasicObject方法內,那麼它是怎麼使用的呢?
實際上咱們調用一個方法,若是對象沒法找不到這個方法,就會調用method_missing方法,咱們舉個例子:
- class SendClass
- def get_one_name
- 'one_name'
- end
-
- def get_two_name
- 'two_name'
- end
-
- def get_three_name
- 'three_name'
- end
-
- def method_missing(name, *argc)
- 'call method_missing'
- end
- end
-
- s = SendClass.new
-
- puts s.send(:get_one_name)
- puts s.send(:get_two_name)
- puts s.send(:get_three_name)
- puts s.send(:get_four_name)
咱們基於這個原理實現一下動態方法:
- class SendClass
- def method_missing(name, *argc)
- if [:one_name, :two_name, :three_name].include?(name)
- name
- else
- super
- end
- end
- end
-
- s = SendClass.new
-
- puts s.one_name
- puts s.two_name
- puts s.three_name
- puts s.four_name
-
通常狀況下會是send, define_method, method_missing三個方法同時使用:
- class SendClass
- [:one_name, :two_name, :three_name].each do |name|
- define_method("get_#{name}"){
- name
- }
- end
-
- def method_missing(name, *argc)
- if self.respond_to?(name)
- send(:name)
- else
- super
- end
- end
- end
-
- s = SendClass.new
-
- puts s.get_one_name
- puts s.get_two_name
- puts s.get_three_name
- puts s.get_four_name
其餘
在最後,咱們講解一些細節
走到method_missing的方法並非真正的方法
舉個例子:
- class SendClass
- def method_missing(name, *argc)
- if [:one_name, :two_name, :three_name].include?(name)
- name
- else
- super
- end
- end
- end
-
- s = SendClass.new
-
- puts s.respond_to?(:one_name)
- puts s.respond_to?(:two_name)
- puts s.respond_to?(:three_name)
method_missing方法是方法調用找不到對應方法的時候就會調用method_missing方法,可是這些方法不會真正被定義,只能算是一個異常機制。
method_missing有性能問題
method_missing雖然好用,可是會有必定的性能損失,爲何呢?還記得咱們說到的方法查找嗎,遵循右上的原則,最終找不到的時候纔會報錯。
- class BasicObject
- def method_missing(name, *argc)
- puts 'call BasicObject method_missing'
- super
- end
- end
-
- class Object
- def method_missing(name, *argc)
- puts 'call Object method_missing'
- super
- end
- end
-
- class SendClass
- def method_missing(name, *argc)
- if [:one_name, :two_name, :three_name].include?(name)
- name
- else
- puts 'call SendClass method_missing'
- super
- end
- end
- end
-
- s = SendClass.new
-
- puts s.four_name
執行這段代碼:
- call SendClass method_missing
- call Object method_missing
- call BasicObject method_missing
- send.rb:4:in `method_missing': class or module required (TypeError)
- from send.rb:11:in `method_missing'
- from send.rb:21:in `method_missing'
- from send.rb:28:in `<main>'
這就是方法查找,若是four_name方法在對象鏈中都找不到,就會查找對象鏈中method_missing方法,逐個調用,直到真正找不到這個方法。方法查找是有必定的性能損失,因此說mthod_missing方法是必定的性能損失。
真正方法的優先級高於method_missing方法
若是一個對象有這個方法,確定不會調用method_missing方法,這個就不介紹了,這也是method_missing方法的用途,若是低於method_missing方法,那麼method_missing還有什麼用呢?
動態方法就介紹到這吧,洗洗睡了。