zoukankan      html  css  js  c++  java
  • [转]ruby之动态方法

    转自:http://blog.csdn.net/feigeswjtu/article/details/51542061

    ruby中的方法是一个很神奇的概念,怎么个神奇法呢,听我慢慢道来。

    在介绍ruby的方法之前,我们先说下什么是静态语言,在编译阶段,编译器都会检查方法调用的对象是否有一个这样的方法,如果没有就直接报错,这种称为静态类型检查,这种语言称为静态语言。众所周知,ruby是动态语言,只有真正调用这个方法的时候,找不到这个方法才会报错错误,方法查找过程可以见我的另一篇文章(ruby之方法查找)。

    到底是哪儿种类型的语言好呢,各有利弊吧,也不能说哪儿个语言好,只能说哪儿个语言适合,比如,一般的动态语言,开发效率很高,但是会有一定的性能损失,静态语言开发效率低,经常要写很多类似set/get方法,但是如果有错误,在编译阶段就能发现,不用在运行中发现错误。在这方面,语言选择个人的理解是初期,需要很快的迭代时可以用动态语言,但是等到稳定后,还是要回归到静态语言。

    闲话不多说了,这篇文章是用来解释ruby中的动态方法的。

    ruby中的动态方法只要跟send, define_method, method_missing这几个方法相关。

    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还有什么用呢?
     
    动态方法就介绍到这吧,洗洗睡了。
  • 相关阅读:
    布尔值数据类型
    数据类型
    while循环。for循环
    对于元素类型为集合的List进行排序
    进程和线程之由来
    ExecutorService的关闭
    Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
    什么是设计模式
    java多线程的原子性
    OutputStream的flush()方法
  • 原文地址:https://www.cnblogs.com/linganxiong/p/6109714.html
Copyright © 2011-2022 走看看