[转]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还有什么用呢?
 
动态方法就介绍到这吧,洗洗睡了。
相关文章
相关标签/搜索