[轉]ruby之動態方法

轉自: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

[ruby]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. class SendClass  
  2.   def get_one_name  
  3.     'one_name'  
  4.   end  
  5.   
  6.   def get_two_name  
  7.     'two_name'  
  8.   end  
  9.   
  10.   def get_three_name  
  11.     'three_name'  
  12.   end  
  13. end  
  14.   
  15. s = SendClass.new  
  16.   
  17. puts s.send(:get_one_name)  #one_name  
  18. puts s.send(:get_two_name)  #two_name  
  19. puts s.send(:get_three_name)#three_name  
  20. puts s.send(:get_four_name) #undefined method `get_four_name'  

send方法至少要有一個參數,這個參數就是要調用的方法名,若是找不到這個方法,就會報undefined method的錯誤,send方法用處也是比較多的,若是有一個對象有不少屬性,可是調用這些屬性方法時,若是不知道要調用的是哪一個屬性,就能夠用這個方法。
 
可是send調用的這些方法仍是必須存在的呀,並非一些動態的方法,只是動態調用,那麼有沒有可能動態定義方法呢?別急,這就是咱們要介紹的define_method方法。

define_method

[ruby]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. class SendClass  
  2.   #def get_one_name  
  3.   #  'one_name'  
  4.   #end  
  5.   
  6.   #def get_two_name  
  7.   #  'two_name'  
  8.   #end  
  9.   
  10.   #def get_three_name  
  11.   #  'three_name'  
  12.   #end  
  13.   [:one_name, :two_name, :three_name].each do |name|  
  14.     define_method("get_#{name}"){  
  15.       name                
  16.     }               
  17.   end               
  18. end                 
  19.   
  20. s = SendClass.new  
  21.   
  22. puts s.send(:get_one_name)  #one_name  
  23. puts s.send(:get_two_name)  #two_name  
  24. puts s.send(:get_three_name)#three_name  
  25. puts s.send(:get_four_name) #undefined method `get_four_name'  

看吧,達到了一樣的效果,只用了一條語句就定義了幾個方法,那麼還有沒有其餘方法能達到這樣的效果呢?答案是確定的,那就是method_missing。

method_missing

method_missing是一個魔鬼方法,使用不當的狀況下,會發生不少意想不到的問題。不過它仍是頗有用的,在介紹它以前,咱們先會議一下方法的查找,遵循右上法則,直到找到BasicObject中。那麼咱們看看BasicObject有沒有這個方法。
[ruby]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. 1.9.3-p551 :016 > BasicObject.method(:method_missing)  
  2.  => #<Method: Class(BasicObject)#method_missing>  
找到了,method_missing方法是定義在BasicObject方法內,那麼它是怎麼使用的呢?
實際上咱們調用一個方法,若是對象沒法找不到這個方法,就會調用method_missing方法,咱們舉個例子:
[ruby]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. class SendClass  
  2.   def get_one_name  
  3.     'one_name'  
  4.   end  
  5.   
  6.   def get_two_name  
  7.     'two_name'  
  8.   end  
  9.   
  10.   def get_three_name  
  11.     'three_name'  
  12.   end  
  13.   
  14.   def method_missing(name, *argc)  
  15.     'call method_missing'  
  16.   end      
  17. end        
  18.   
  19. s = SendClass.new  
  20.   
  21. puts s.send(:get_one_name)  #one_name  
  22. puts s.send(:get_two_name)  #two_name  
  23. puts s.send(:get_three_name)#three_name  
  24. puts s.send(:get_four_name) #call method_missing  
咱們基於這個原理實現一下動態方法:
[ruby]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. class SendClass  
  2.   def method_missing(name, *argc)  
  3.     if [:one_name, :two_name, :three_name].include?(name)  
  4.       name  
  5.     else #處理不了的方法就讓父類處理  
  6.       super  
  7.     end  
  8.   end  
  9. end  
  10.   
  11. s = SendClass.new  
  12.   
  13. puts s.one_name    #one_name  
  14. puts s.two_name    #two_name  
  15. puts s.three_name  #three_name  
  16. puts s.four_name   #undefined method `four_name'  
  17.                                                            
 
通常狀況下會是send, define_method, method_missing三個方法同時使用:
[ruby]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. class SendClass  
  2.   [:one_name, :two_name, :three_name].each do |name|  
  3.     define_method("get_#{name}"){  
  4.       name  
  5.     }   
  6.   end  
  7.   
  8.   def method_missing(name, *argc)  
  9.     if self.respond_to?(name)  
  10.       send(:name)  
  11.     else #處理不了的方法就讓父類處理  
  12.       super  
  13.     end  
  14.   end  
  15. end  
  16.   
  17. s = SendClass.new  
  18.   
  19. puts s.get_one_name    #one_name  
  20. puts s.get_two_name    #two_name  
  21. puts s.get_three_name  #three_name  
  22. puts s.get_four_name   #undefined method `get_four_name'  

其餘

在最後,咱們講解一些細節

走到method_missing的方法並非真正的方法

舉個例子:
[ruby]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. class SendClass  
  2.   def method_missing(name, *argc)  
  3.     if [:one_name, :two_name, :three_name].include?(name)  
  4.       name        
  5.     else #處理不了的方法就讓父類處理  
  6.       super  
  7.     end  
  8.   end  
  9. end     
  10.       
  11. s = SendClass.new  
  12.       
  13. puts s.respond_to?(:one_name)#false  
  14. puts s.respond_to?(:two_name)#false  
  15. puts s.respond_to?(:three_name)#false  
method_missing方法是方法調用找不到對應方法的時候就會調用method_missing方法,可是這些方法不會真正被定義,只能算是一個異常機制。

method_missing有性能問題

method_missing雖然好用,可是會有必定的性能損失,爲何呢?還記得咱們說到的方法查找嗎,遵循右上的原則,最終找不到的時候纔會報錯。
[ruby]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. class BasicObject  
  2.   def method_missing(name, *argc)  
  3.     puts 'call BasicObject method_missing'  
  4.     super  
  5.   end  
  6. end  
  7.   
  8. class Object  
  9.   def method_missing(name, *argc)  
  10.     puts 'call Object method_missing'  
  11.     super  
  12.   end  
  13. end  
  14.   
  15. class SendClass  
  16.   def method_missing(name, *argc)  
  17.     if [:one_name, :two_name, :three_name].include?(name)  
  18.       name  
  19.     else #處理不了的方法就讓父類處理  
  20.       puts 'call SendClass method_missing'  
  21.       super  
  22.     end  
  23.   end  
  24. end  
  25.   
  26. s = SendClass.new  
  27.   
  28. puts s.four_name  
 
執行這段代碼:
[plain]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. call SendClass method_missing  
  2. call Object method_missing  
  3. call BasicObject method_missing  
  4. send.rb:4:in `method_missing': class or module required (TypeError)  
  5.     from send.rb:11:in `method_missing'  
  6.     from send.rb:21:in `method_missing'  
  7.     from send.rb:28:in `<main>'  

這就是方法查找,若是four_name方法在對象鏈中都找不到,就會查找對象鏈中method_missing方法,逐個調用,直到真正找不到這個方法。方法查找是有必定的性能損失,因此說mthod_missing方法是必定的性能損失。

真正方法的優先級高於method_missing方法

若是一個對象有這個方法,確定不會調用method_missing方法,這個就不介紹了,這也是method_missing方法的用途,若是低於method_missing方法,那麼method_missing還有什麼用呢?
 
動態方法就介紹到這吧,洗洗睡了。
相關文章
相關標籤/搜索