Menu
CoddyTech

鸭子类型

本课属于 Coddy Ruby 学习旅程的“面向对象编程”单元。第 33 课,共 57 课。

Ruby 在调用方法前不会检查对象的类;它只要求对象拥有该方法。“如果它走起路来像鸭子,叫起来也像鸭子,那它就是鸭子。”这就是鸭子类型:没有共同超类的类仍然可以在同一位置使用:

class Duck
  def speak
    "Quack"
  end
end

class Robot
  def speak
    "Beep"
  end
end

class Radio
  def speak
    "...news..."
  end
end

[Duck.new, Robot.new, Radio.new].each { |thing| puts thing.speak }

输出:

Quack
Beep
...news...

内置类也能参与其中。只使用 << 的方法可以用于数组、字符串,或任何定义了 << 的对象:

class Tally
  attr_reader :count

  def initialize
    @count = 0
  end

  def <<(_item)
    @count += 1
    self
  end
end

def export(items, target)
  items.each { |item| target << item.to_s }
  target
end

p export([1, 2], [])
p export([1, 2], +"")
puts export([1, 2, 3], Tally.new).count

输出:

["1", "2"]
"12"
3

respond_to?(:name) 用来检查一个对象是否有公共方法,但不会调用该方法。当对象可能支持也可能不支持某项可选功能时,可以使用它:

class Printer
  def print_page(text)
    "printing #{text}"
  end
end

class Scanner
end

[Printer.new, Scanner.new].each do |device|
  if device.respond_to?(:print_page)
    puts device.print_page("report")
  else
    puts "#{device.class} cannot print"
  end
end

输出:

printing report
Scanner cannot print

如果对象没有这个方法,调用就会引发 NoMethodError,错误消息会指出方法和对象。鸭子类型依赖于传入的每个对象都能响应发送给它的方法:

def loudest(things)
  things.map(&:speak).max_by(&:size)
end

class Cat
  def speak
    "Meow"
  end
end

puts loudest([Cat.new, Cat.new])
begin
  loudest([Cat.new, 42])
rescue NoMethodError => e
  puts e.message
end

输出:

Meow
undefined method `speak' for an instance of Integer
challenge icon

挑战

简单

在 functions.rb 中完成 Printer 类、LineCounter 类和辅助函数 export(items, target)。export 会通过 target << item.to_s 将每个项目发送到目标,并返回目标。Printer#<< 会为文本加上行号并输出,例如 1: text,然后返回打印机。LineCounter#<< 会统计行数和字符数,并返回计数器;它的 to_s 会返回 3 lines, 12 chars。

提供的代码会读取项目,将它们分别导出到数组(用 p 打印)、新字符串(用 p 打印)、Printer 和 LineCounter(用 puts 打印)。

你的代码要写在 printer.rb、line_counter.rb 和 functions.rb 中。main.rb 包含提供的输入/输出代码,不能编辑。

亲手试试

require_relative 'printer'
require_relative 'line_counter'
require_relative 'functions'

# 提供的输入/输出代码:保持原样。
input = []
while (line = gets)
  input << line.chomp
end
p export(input, [])
p export(input, +"")
export(input, Printer.new)
puts export(input, LineCounter.new)
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Ruby 在线编译器