Tính đa hình
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 32 trên 57.
Tính đa hình nghĩa là một lời gọi phương thức hoạt động với các đối tượng thuộc những lớp khác nhau, và mỗi đối tượng phản hồi theo cách riêng. Đoạn code lặp qua các hình gọi area trên từng hình mà không cần hỏi đó là hình gì:
class Shape
def to_s
format("%s %.2f", self.class.name.downcase, area)
end
end
class Circle < Shape
def initialize(r)
@r = r
end
def area
3.14159 * @r * @r
end
end
class Square < Shape
def initialize(side)
@side = side
end
def area
@side * @side
end
end
shapes = [Circle.new(1), Square.new(3)]
shapes.each { |s| puts s }
puts format("total %.2f", shapes.sum(&:area))Đầu ra:
circle 3.14
square 9.00
total 12.14Nếu không có tính đa hình, bên gọi phải kiểm tra kiểu của từng đối tượng và lặp lại việc kiểm tra đó ở mọi nơi mà sự khác biệt này có ảnh hưởng. Khi có tính đa hình, sự khác biệt nằm bên trong mỗi lớp:
# không dùng tính đa hình
case shape.kind
when "circle" then 3.14159 * shape.r ** 2
when "square" then shape.side ** 2
end
# dùng tính đa hình
shape.areaVì vậy, khi thêm một loại đối tượng mới, bạn không cần thay đổi code gọi nó: một lớp con mới có phương thức area riêng sẽ hoạt động trong mọi vòng lặp đã có:
class Shape
def describe
"area #{area}"
end
end
class Square < Shape
def area
4
end
end
class Triangle < Shape
def area
6
end
end
[Square.new, Triangle.new].each { |s| puts s.describe }Đầu ra:
area 4
area 6to_s là ví dụ thường gặp: puts gọi phương thức này trên mọi đối tượng, và mỗi lớp phản hồi theo cách khác nhau. Lớp của bạn cũng có thể tham gia bằng cách định nghĩa to_s riêng:
class Coin
def to_s
"a coin"
end
end
[42, "text", nil, [1, 2], Coin.new].each { |x| puts "[#{x}]" }Đầu ra:
[42]
[text]
[]
[[1, 2]]
[a coin]Thử thách
DễHoàn thiện các hình. Shape#to_s trả về circle: 3.14, tên lớp viết thường và diện tích với hai chữ số thập phân, còn mỗi lớp con cung cấp area: Circle.new(r) (dùng 3.14159 cho số pi), Rectangle.new(w, h) và Triangle.new(base, height).
Code được cung cấp đọc các dòng circle 1, rectangle 2 3 hoặc triangle 4 5, in ra từng hình, sau đó in total: 22.14 và largest: rectangle: 6.00, chỉ dùng to_s và area.
Code của bạn nằm trong các tệp shape.rb, circle.rb, rectangle.rb và triangle.rb. main.rb chứa code đầu vào/đầu ra được cung cấp và không thể chỉnh sửa.
Tự làm thử
require_relative 'shape'
require_relative 'circle'
require_relative 'rectangle'
require_relative 'triangle'
# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
input = []
while (line = gets)
input << line.chomp
end
kinds = { "circle" => Circle, "rectangle" => Rectangle, "triangle" => Triangle }
shapes = input.map do |line|
kind, *numbers = line.split
kinds[kind].new(*numbers.map(&:to_f))
end
shapes.each { |s| puts s }
puts format("total: %.2f", shapes.sum(&:area))
puts "largest: #{shapes.max_by(&:area)}"
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