MetaProgramming Chapter 3

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
  end
end
a = A.new
a.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

  true
end

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
  end
end

method_missing

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

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

未覆写BasicObject#method_missing:

```

当调用到不存在的方法时,会调用method_missing方法

```ruby
class Lawyer; end

nick = Lawyer.new
nick.talk_simple

< NoMethodError: undefined method `talk_simple' for #<Lawyer:0x007f801aa81938>`




<div class="se-preview-section-delimiter"></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}"
  end
end




<div class="se-preview-section-delimiter"></div>
number_of = Roulette.new
puts number_of.bob
puts number_of.frank

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

白板类

BasicObject

NOTE: method_missing的另一个问题

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

删除方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值