MetaProgramming Chapter 3

2016-11-21 19:02:29来源:CSDN作者:axu_air人点击

第七城市

MetaProgramming Chapter 3

Ruby 元编程 第三章

该文档包含Ruby方法的介绍。

阅读文档,你将学到:

  • 动态方法的使用
  • 白板类的介绍

动态方法 Dynamic Method

NOTE:
This technique of defining a method at runtime is called a Dynamic Method
在运行时定义方法的技术称为 动态方法

动态调用方法

class A  def a_method(x)  puts x  endend
a = A.newa.a_method(1)a.send(:a_method, 1)

NOTE: Object#send
第一个参数:方法名字(String/Symbol类型)
剩下的参数和代码块会直接传递给调用方法

  • 为什么使用send方法?
    send方法替代点标识符(.)调用方法
    send方法中将调用的方法名变成参数,这样就可以在代码运行的最后一刻决定调用哪个方法.即:动态派发(Dynamic Dispatch)

  • 动态派发

def refresh(options={})  defaults  = {}  attributes = [ :input, :output, :commands, :print, :quiet,                 :exception_handler, :hooks, :custom_completions,                 :prompt, :memory_size, :extra_sticky_locals ]  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  trueend

NOTE: Kernel#respond_to?方法检测诸如Pry#memory_size=这种方法是否存在
若存在则返回true

============QUESTION===============
Kernel#respond_to?

def respond_to?(name)  `!!#{self}['$' + name]`end

私有性问题

NOTE:
send可以调用任何方法,包括私有方法
如果不用这种破坏私有性的行为,则使用public_send方法

动态定义方法

NOTE:Module#defind_method()方法可以替代def关键字随时定义一个方法, 允许在运行时决定方法名字

重构Computer类

  • 添加动态派发:在代码的最后决定调用的方法
  • 动态创建方法:在运行时,在类内部定义方法
  • 内省方式缩减代码:传入参数,动态调用
class Computer  def initialize(computer_id, data_source)    @id = computer_id    @data_source = data_source    data_source.methods.grep(/^get_(.*)_info$/) { Computer.define_component $1  }  end  def self.define_component(name)    define_method(name) do      # ...    end  endend

method_missing

NOTE: method_missing:BasicObject的私有实例方法,所有对象都继承自BasicObject类,对所有对象都可用
NOTE: 覆写method_missing方法:截获无主信息,调用实际上不存在的方法

===========QUESTION==============
如何做到找不到调用方法就调用method_missing方法?
查看method_missing 方法

未覆写BasicObject#method_missing:

```当调用到不存在的方法时,会调用method_missing方法```rubyclass Lawyer; endnick = Lawyer.newnick.talk_simple< NoMethodError: undefined method `talk_simple' for #<Lawyer:0x007f801aa81938>`<div></div>

幽灵方法

NOTE: 被method_missing方法处理的消息,称为 幽灵方法

动态代理

NOTE: 可以捕获幽灵方法并把它转发给其他对象,称为 动态代理

===========QUESTION==============
Kernel:respond_to_missing? 与respond_to? 的关系

method_missing 的问题

WARNING: 由于调用未定义的方法会导致调用 method_missing 方法, 所以对象可能会接受错误的方法调用
如:写错方法名

class Roulette  def method_missing(name, *args)    person = name.to_s.capitalize    3.times do    number = rand(10) + 1    puts "#{number}..."  end  "#{person} got a #{number}"  endend<div class="se-preview-section-delimiter"></div>
number_of = Roulette.newputs number_of.bobputs number_of.frank

ERROR:
由于number超出在方法内定义的作用域范围(times..end)被当成Roulette#number 而非变量,
且 未定义方法:Roulette#number,故循环调用Roulette#method_missing,直到调用堆栈溢出

白板类

BasicObject

NOTE: method_missing的另一个问题

NOTE: 白板类: 拥有极少方法的类
NOTE: 如果祖先链上有与重写的幽灵方法命名冲突,幽灵方法将被忽略,故若继承白板类,则不会出现此情况

删除方法

第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台