Ruby 基础+进阶 第三天

本文介绍了Ruby中的动态调用方法、动态定义方法、幽灵方法method_missing、respond_to_missing?、const_missing、白板类以及删除方法的使用,探讨了动态方法与Method_missing的使用原则。
摘要由CSDN通过智能技术生成

本次学习资料

通读《Ruby元编程》,记录下有趣的以及不明白的知识点

动态调用方法

在Ruby中通过Object#send方法可以代替点标识调用对象的指定实例方法,通过直接调用和使用send方法调用得到的结果是一样的,使用send的好处是,可以在编码中,动态的决定方法调用。这个技巧在元编程中被称为动态派发

class MyClass
    def my_method(my_arg)
        my_arg * 2
    end
end

obj = MyClass.new
obj.my_method(3)   
obj.send(:my_method, 3) 
#=> 6
#=> 6
def refresh(options={})
  defaults = {}
  attributes = [:input, :commands, :print, :quiet, :hooks, :memory_size]
  attributes.each do |attribute|
    defaults[attribute] = Pry.send attribute
  end
  defaults.merge!options.each do |key,value|
    send("#{key}=",value) if respond_to?("#{key}")
  end
end

另外需要指出的地方是通过Object#send不仅可以调用公共方法,也可以调用对象的私有方法。如果想保留对象的封装特性,不向外暴露私有方法可以使用Object#public_send方法。

动态定义方法

除了方法的动态调用之外,Ruby还通过Module#define_method方法和代码块提供了动态方法定义方式


class MyClass
    define_method :my_method do |my_arg|
        my_arg * 3
    end
end

obj = MyClass.new
obj.my_method(2)  #=> 6

上面代码通过define_method方法取代了关键词def,其本质上都是相同的,只是在定义方式上,define_method的方式更加灵活一些,可以通过在编码中通过推导,完成函数的定义,增加了实现的灵活性。

class Computer
  def initialize(computer_id, data_source)
    @id = computer_id
    @data_source = data_source
    data_source.methods.grep(/^get_(.*)_info$/) do
      Computer.define_component $1
    end
  end
  def self.define_component(name)
    define_method(name) do
      info = @data_source.send("get_#{name}_info",@id)
      price = @data_source.send("get_#{name}_price",@id)
      result = "#{name.capitalize}: #{info} ($#{price})"
      return "* #{result}" if price >= 100
      result
    end
  end
end


幽灵方法与动态代理

幽灵方法即method_missing,method_missing是BasicObject的一个私有实例方法,可以通过send调用。

严格意义上讲method_missing方法,并不算是明确的定义(不会出现在methods列表中),其本质是通过方法查找的机制来截获调用信息进而合理的给出相应方法的回应。有点类似与异常处理中的抛出异常,一层一层的往外抛。

method_missing利用的机制是,当一个对象进行某个方法调用的时候,会到其对应的类的实例方法中进行查找,如果没有找到,则顺着祖先链向上查找,直到找到BasicObject类为止。如果都没有则会最终调用一个BasicObject#method_missing抛出NoMethodError异常。

当我们需要定义很多相似的方法时候,可以通过重写method_missing方法,对相似的方法进行统一做出回应,这样一来其行为就类似与调用定义过的方法一样。

class Lawer
  def method_missing(symbol, *args)
    puts "you called: #{symbol}(#{args.join(',')})"
    puts "(you also passed it a block)" if block_given?
  end
end

even = Lawer.new
even.talk_simple('a','b') do
  # a block
end

#=>you called: talk_simple(a,b)
#=>(you also passed it a block)
class Roulette
  def method_missing(name, *args)
    person = name.to_s.capitalize
    super unless %w[Bob Frank Bill Honda Eric].include? person
    number = 0
    3.times do
      number = rand(10) + 1
      puts "#{number}..."
    end
    "#{person} got a #{number}"
  end
end

number_of = Roulette.new
puts number_of.bob
puts number_of.kitty

对一些封装过的对象,通过method_missing方法收集调用,并把这些调用转发到被封装的对象,这一过程称为动态代理,其中method_missing体现了动态,转发体现了代理。

respond_to_missing?

respond_to_missing?方法是为了解决respond_to?无法正确识别幽灵方法的问题,需要对它进行覆写。

respond_to_missing?方法的代码与method_missing的类似,它首先查看一个方法是不是一个幽灵方法,若是,返回true,若不是,调用super,super就是默认的Object#respond_to_missing?方法,它总是返回false。


class Computer < BasicObject
  def initialize(computer_id, data_source)
    @id = computer_id
    @data_source = data_source
  end

  def method_missing(name)
    super if !@data_source.respond_to?("get_#{name}_info")
    info = @data_source.send("get_#{name}_info",@id)
    price = @data_source.send("get_#{name}_price",@id)
    result = "#{name.capitalize}: #{info} ($#{price})"
    return "* #{result}" if price >= 100
    result
  end

  def respond_to_missing?(method, include_private = false)
    @data_source.respond_to?("get_#{method}_info") || super
  end

end

正确地做法是每次覆写method_missing的同时覆写respond_to_missing?方法。

const_missing

与method_missing类似,还有关于常量的const_missing方法,当引用一个不存在的常量时,Ruby会把这个常量名作为一个符号传递给const_missing方法。

白板类与删除方法

使用method_missing后,可能会出现程序员希望某个方法能用method_missing实现,但其祖先链上已经有类实现了这个方法导致bug的问题,因此为了避免此问题,需要一个让其所在的类继承一个拥有极少方法的类,那种类就是白板类(blank slates)。

拥有极少方法的类称为白板类,通过继承BasicObject类,可以迅速的得到一个白板类。除了这种方法以外,还可以通过删除方法来将一个普通类变为白板类。

删除某个方法有两种方式:

Module#undef_method
Module#remove_method
二者的区别是Module#undef_method会删除所有(包括继承而来的)方法。而Module#remove_method只删除接受者自己的方法,而保留继承来的方法。

动态方法与Method_missing的使用原则

当可以使用动态方法时候,尽量使用动态方法。
除非必须使用method_missing方法(方法特别多或者方法名未知的情况),否则尽量少使用它。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值