ruby 中的 forwardable 模块(1)

forwardable模块

最近在看rack-attack源码的过程中,看到了这个模块。ruby的语法糖实在是太丰富了,各种特性看得人一脸懵逼。幸好各位前辈留下了许多文章去给我们指点迷津,这篇文章对forwardable的描述我觉得很通俗异动,很适合我们对这个模块有一个比较浅显的理解。

Forwardable 是一个模块,中文名翻译为委托,包含了一些方法可用于向给定类的所有实例添加行为 ( behavior )。

在 Ruby 内部实现时,使用了 extend 关键字将此模块包含进 singleton 类,为此可以在类级别向类添加方法 ( 保持简单 )。简单来说,就是利用Ruby 提供的消息转发系统避免了创建一个 getter 方法来访问我们所需要的元素。

def_delegator 方法

在介绍forwardable的这些API方法之前,我们先举一些简单的例子来给看看我们为什要使用这些方法,观察一下这些方法给我带来了什么便利。

class Hero
  attr :skills
  def initialize
    @skills = [:strong, :keen, :brave]
  end
end

jack = Hero.new
puts "Jack's main skill: #{jack.skills.first}"

运行结果如下:

Jack's main skill: strong

这能正常工作,但, 在 Hero 类定义之外调用 jack.skills.first 稍微有点冗余,我们可以first方法进行一次封装。因此,让我们将此代码封装到 Hero 类定义中:

class Hero
  attr :skills

  def initialize
    @skills = [:strong, :keen, :brave]
  end

  def main_skill
    @skills.first
  end
end

jack = Hero.new
puts "Jack's main skill: #{jack.main_skill}"

在这里,我们会得到和不封装前一样的输出。但是ruby提供了一种更优雅的机制,Forwardable#def_delegator 方法允许对象将消息转发到定义的接收器,我们现在先按照这种机制修改一下代码:

require 'forwardable'

class Hero
  attr :skills
  extend Forwardable

  def_delegator :@skills, :first, :main_skill

  def initialize
    @skills = [:strong, :keen, :brave]
  end
end

jack = Hero.new
puts "Jack's main skill: #{jack.main_skill}"

上面的代码中

  1. 首先,我们加载了 forwardable 模块
  2. 其次,我们使用 extend Forwardable 关键字将该模块的方法添加到 Hero 类级别中
  3. 最后,我们使用新添加的类级方法 def_delegator,我们的实例子jack是我们的对象,是我们的转发者。第一个参数 :@skills 对应于消息转发的接收者,第二个参数 :first 是要转发的消息,最后是第三个参数 :main_skill 是 :first 消息的别名。当我们调用 jack.main_skill 时, 它比 jack.first 更可读,然后在内部自动调用 skills.first。

def_delegators 方法

def_delegators 方法与 def_delegator 方法类似,两个方法的主要区别是,def_delegators 方法需要一组方法来转发,并且方法不能别名。

require 'forwardable'
class Todolist
  attr :tasks

  extend Forwardable

  def_delegators :@tasks, :first, :last

  def initialize
    @tasks = %w[conception implementation refactoring]
  end
end

todolist = Todolist.new
puts "first tasks: #{todolist.first}"
puts "last  tasks: #{todolist.last}"

输出结果为:

first tasks: conception
last  tasks: refactoring

在这个示例中,tasks 数组的 firt 和 last 方法可用于任何 Todolis 实例, 当调用这两个方法其中之一时,消息将被转发到 tasks 数组。

delegate 方法

delegate 方法接受一个散列 ( hash ) 作为参数,其中键 ( key ) 是一条或多条消息,
值 ( value ) 是 键对应的消息的接收器。

require 'forwardable'

class Computer
  attr :cores, :screens

  extend Forwardable

  delegate %I[size]   => :@cores,
           %I[length] => :@screens

def initialize
    @cores  = (1..8).to_a
    @screens = [1, 2]
  end
end

macrosoft = Computer.new
puts "Cores:   #{macrosoft.size}"
puts "Screens: #{macrosoft.length}"

输出结果为:

Cores:   8
Screens: 2

上面的示例中,macrosoft.size 消息对应于 macrosoft.cores.size,macrosoft.length 消息对应于 macrosoft.screens.length。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值