Menu
Coddy logo textTech

ダックタイピング

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

チャレンジ

簡単

Printer クラスと LineCounter クラス、および functions.rb 内のヘルパー 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オンラインコンパイラ