ruby之动态方法

原创 2016年05月31日 23:13:44

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

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

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

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

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

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)  #one_name
puts s.send(:get_two_name)  #two_name
puts s.send(:get_three_name)#three_name
puts s.send(:get_four_name) #undefined method `get_four_name'

send方法至少要有一个参数,这个参数就是要调用的方法名,如果找不到这个方法,就会报undefined method的错误,send方法用处也是比较多的,如果有一个对象有很多属性,但是调用这些属性方法时,如果不知道要调用的是哪个属性,就可以用这个方法。

但是send调用的这些方法还是必须存在的呀,并不是一些动态的方法,只是动态调用,那么有没有可能动态定义方法呢?别急,这就是我们要介绍的define_method方法。

define_method

class SendClass
  #def get_one_name
  #  'one_name'
  #end

  #def get_two_name
  #  'two_name'
  #end

  #def get_three_name
  #  'three_name'
  #end
  [: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)  #one_name
puts s.send(:get_two_name)  #two_name
puts s.send(:get_three_name)#three_name
puts s.send(:get_four_name) #undefined method `get_four_name'

看吧,达到了同样的效果,只用了一条语句就定义了几个方法,那么还有没有其他方法能达到这样的效果呢?答案是肯定的,那就是method_missing。

method_missing

method_missing是一个魔鬼方法,使用不当的情况下,会发生很多意想不到的问题。不过它还是很有用的,在介绍它之前,我们先会议一下方法的查找,遵循右上法则,直到找到BasicObject中。那么我们看看BasicObject有没有这个方法。
1.9.3-p551 :016 > BasicObject.method(:method_missing)
 => #<Method: Class(BasicObject)#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)  #one_name
puts s.send(:get_two_name)  #two_name
puts s.send(:get_three_name)#three_name
puts s.send(:get_four_name) #call 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.one_name    #one_name
puts s.two_name    #two_name
puts s.three_name  #three_name
puts s.four_name   #undefined method `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    #one_name
puts s.get_two_name    #two_name
puts s.get_three_name  #three_name
puts s.get_four_name   #undefined method `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)#false
puts s.respond_to?(:two_name)#false
puts s.respond_to?(:three_name)#false
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还有什么用呢?

动态方法就介绍到这吧,洗洗睡了。






版权声明:本文为博主原创文章,转载请注明出处,欢迎大家来一起交流。 https://blog.csdn.net/feigeswjtu/article/details/51542061

Ruby China中method_missing和define_method剖析

Ruby China中method_missing和define_method剖析 很多人都与 method_missing 干柴烈火,但在并没有小心处理彼此之间的关系。所以,我...
  • sunyllove
  • sunyllove
  • 2016-08-26 17:21:01
  • 743

浅析 Ruby 里的几个动态方法 (一),send 方法

浅析 Ruby 里的几个动态方法 (一),send 方法 第一次写技术方面的贴,欢迎拍砖 :) 动态方法,是去控制静态方法的方法,让静态方法的方法名和方法的内容会根据参...
  • sunyllove
  • sunyllove
  • 2016-07-01 16:08:05
  • 1945

ruby之动态方法

ruby动态方法介绍,包括send, define_method, method_missing方法介绍。
  • feigeswjtu
  • feigeswjtu
  • 2016-05-31 23:13:44
  • 471

Ruby如何实现动态方法调用?

在Ruby中,有多种方法可以实现方法的动态调用。 1. 使用send方法 第一种实现动态方法调用是使用send方法,send方法在Object类中定义,方法的第一个参数是一个符号用来表示所要调用的...
  • dazhi_100
  • dazhi_100
  • 2014-04-25 18:51:03
  • 774

ruby on rails 如何实现动态方法调用?

在Ruby中,有多种方法可以实现方法的动态调用。 1. 使用send方法 第一种实现动态方法调用是使用send方法,send方法在Object类中定义,方法的第一个参数是一个符号用来表示所要调...
  • zhihuichina
  • zhihuichina
  • 2017-05-12 12:34:00
  • 464

ruby定义属性内建方法

在Ruby中,定义属性的标识符有4个,分别是attr, attr_reader, attr_writer, attr_accessor,这些不是Ruby的关键字,而是一些内建的方法,使用语法如下: ...
  • li6151770
  • li6151770
  • 2016-10-10 21:31:14
  • 204

Ruby学习笔记4: 动态web app的建立

Our Model is fine for now. Let's prepare our Migration table. We do this in two steps: a. We add co...
  • sonictl
  • sonictl
  • 2015-05-07 23:38:19
  • 791

Ruby学习笔记6: 动态web app的建立(3)--多Model之间的交互

前面我们完成的ruby on rails app 的静态、动态网页开发,实现了数据库增删改查的操作,熟悉了MVC结构的工作机理。本文在前面的基础上,要新增一个Prodect的Model,从而体现多mo...
  • sonictl
  • sonictl
  • 2015-05-19 14:49:50
  • 997

Ruby 中Debug工具

Ruby 中Debug工具 byebug pry-byebug
  • XIAO_XIAO_C
  • XIAO_XIAO_C
  • 2017-06-27 21:56:08
  • 199

Ruby之旅(四) 类的继承

Ruby之旅(四) 类的继承 直接上代码 Rectangle.rb class Rectangle   attr_accessor :width   attr_accessor ...
  • sunyllove
  • sunyllove
  • 2016-09-07 14:37:41
  • 738
收藏助手
不良信息举报
您举报文章:ruby之动态方法
举报原因:
原因补充:

(最多只允许输入30个字)