Duck typing
Thuộc phần Lập trình hướng đối tượng trong hành trình Ruby của Coddy. Bài học 33 trên 57.
Ruby không kiểm tra lớp của một đối tượng trước khi gọi phương thức, nó chỉ cần đối tượng đó có phương thức ấy. “Nếu nó đi như vịt và kêu như vịt thì nó là vịt.” Đây là kiểu vịt: các lớp không có lớp cha chung vẫn có thể được dùng ở cùng một vị trí:
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 }Đầu ra:
Quack
Beep
...news...Các lớp có sẵn cũng tham gia. Một phương thức chỉ dùng << sẽ hoạt động với mảng, chuỗi hoặc bất kỳ đối tượng nào của bạn định nghĩa <<:
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Đầu ra:
["1", "2"]
"12"
3respond_to?(:name) kiểm tra xem một đối tượng có phương thức công khai hay không mà không gọi phương thức đó. Dùng cách này khi một đối tượng có thể hỗ trợ hoặc không hỗ trợ một tính năng tùy chọn:
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Đầu ra:
printing report
Scanner cannot printKhi đối tượng không có phương thức đó, lời gọi sẽ gây ra NoMethodError, thông báo lỗi này nêu tên phương thức và đối tượng. Duck typing dựa vào việc mọi đối tượng được truyền vào đều trả lời được các phương thức mà nó nhận được:
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Đầu ra:
Meow
undefined method `speak' for an instance of IntegerThử thách
DễHoàn thiện các lớp Printer và LineCounter cùng hàm hỗ trợ export(items, target) trong functions.rb. export gửi từng phần tử đến target bằng target << item.to_s rồi trả về target. Printer#<< in văn bản kèm số dòng theo dạng 1: text rồi trả về printer. LineCounter#<< đếm số dòng và ký tự rồi trả về bộ đếm; to_s trả về 3 lines, 12 chars.
Code được cung cấp đọc các phần tử rồi xuất chúng ra một mảng (được in bằng p), một chuỗi mới (được in bằng p), một Printer và một LineCounter (được in bằng puts).
Code của bạn nằm trong printer.rb, line_counter.rb và functions.rb. main.rb chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.
Tự làm thử
require_relative 'printer'
require_relative 'line_counter'
require_relative 'functions'
# Đoạn code đầu vào/đầu ra đã được cung cấp: giữ nguyên nhé
input = []
while (line = gets)
input << line.chomp
end
p export(input, [])
p export(input, +"")
export(input, Printer.new)
puts export(input, LineCounter.new)
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Lớp và đối tượng
Làm việc với tệpĐịnh nghĩa lớpBiến instancePhương thức và selfto_s và inspectÔn tập: thẻ thư viện4Tính đóng gói
Phương thức riêng tưPhương thức được bảo vệPhương thức thiết lập riêng tưBảo vệ trạng tháiÔn tập: máy bán hàng tự động7Tính đa hình và duck typing
Tính đa hìnhDuck typingPhương thức trừu tượngcase và lớpÔn tập: phương thức thanh toán2Phương thức truy cập và self
Phương thức đọc và ghiPhương thức gán tùy chỉnhself trong phương thứcĐịnh danh và tính bằng nhauÔn tập: Danh sách liên hệTự luyện tập: Trình biên dịch Ruby online