Menu
CoddyTech

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.14

Nế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.area

Vì 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 6

to_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]
challenge icon

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)}"
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch Ruby online