Menu
Coddy logo textTech

Typowanie kacze

Część sekcji Programowanie obiektowe ścieżki Ruby w Coddy. Lekcja 33 z 57.

Ruby nie sprawdza klasy obiektu przed wywołaniem metody; obiekt musi jedynie mieć tę metodę. „Jeśli chodzi jak kaczka i kwacze jak kaczka, to jest kaczką”. To typowanie kacze: klasy, które nie mają wspólnej nadklasy, nadal mogą być używane w tym samym miejscu:

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 }

Wynik:

Quack
Beep
...news...

Wbudowane klasy również biorą w tym udział. Metoda, która używa tylko <<, działa z tablicą, ciągiem znaków lub dowolnym obiektem, który zdefiniujesz i który ma zdefiniowane <<:

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

Wynik:

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

respond_to?(:name) sprawdza, czy obiekt ma publiczną metodę, nie wywołując jej. Używaj go, gdy obiekt może obsługiwać opcjonalną funkcję lub nie:

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

Wynik:

printing report
Scanner cannot print

Gdy obiekt nie ma danej metody, wywołanie powoduje błąd NoMethodError, którego komunikat wskazuje nazwę metody i obiektu. Duck typing opiera się na tym, że każdy przekazany obiekt odpowiada na wysyłane do niego metody:

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

Wynik:

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

Wyzwanie

Łatwy

Uzupełnij klasy Printer i LineCounter oraz funkcję pomocniczą export(items, target) w pliku functions.rb. export wysyła każdy element do celu za pomocą target << item.to_s i zwraca cel. Printer#<< wypisuje tekst z numerem wiersza w formacie 1: text i zwraca drukarkę. LineCounter#<< zlicza wiersze i znaki oraz zwraca licznik; jego metoda to_s zwraca 3 lines, 12 chars.

Dostarczony kod odczytuje elementy i eksportuje je do tablicy (wypisywanej za pomocą p), do nowego ciągu znaków (wypisywanego za pomocą p), do obiektu Printer oraz do obiektu LineCounter (wypisywanego za pomocą puts).

Twój kod umieść w plikach printer.rb, line_counter.rb i functions.rb. Plik main.rb zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.

Spróbuj swoich sił

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

# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
input = []
while (line = gets)
  input << line.chomp
end
p export(input, [])
p export(input, +"")
export(input, Printer.new)
puts export(input, LineCounter.new)
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Ruby online