Menu
Coddy logo textTech

Полиморфизм

Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 32 из 57.

Полиморфизм означает, что один вызов метода работает с объектами разных классов, и каждый объект отвечает по-своему. Код, который перебирает фигуры в цикле, вызывает area для каждой из них, не спрашивая, к какому типу фигуры она относится:

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))

Результат:

circle 3.14
square 9.00
total 12.14

Без полиморфизма вызывающему коду приходится проверять тип каждого объекта и повторять эту проверку везде, где важна разница. С ним различие находится внутри каждого класса:

# без полиморфизма
case shape.kind
when "circle" then 3.14159 * shape.r ** 2
when "square" then shape.side ** 2
end

# с полиморфизмом
shape.area

Добавление нового типа объекта не требует изменений в вызывающем коде: новый подкласс со своим area работает в каждом уже существующем цикле:

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 }

Вывод:

area 4
area 6

to_s — повседневный пример: puts вызывает его для любого объекта, и каждый класс отвечает по-своему. Ваши классы тоже участвуют, определяя собственный to_s:

class Coin
  def to_s
    "a coin"
  end
end

[42, "text", nil, [1, 2], Coin.new].each { |x| puts "[#{x}]" }

Вывод:

[42]
[text]
[]
[[1, 2]]
[a coin]
challenge icon

Задание

Легко

Реализуйте фигуры. Shape#to_s возвращает circle: 3.14: имя класса в нижнем регистре и площадь с двумя знаками после запятой, а каждый подкласс предоставляет area: Circle.new(r) (используйте 3.14159 для числа пи), Rectangle.new(w, h) и Triangle.new(base, height).

Предоставленный код считывает строки circle 1, rectangle 2 3 или triangle 4 5, выводит каждую фигуру, затем total: 22.14 и largest: rectangle: 6.00, работая только с to_s и area.

Ваш код должен находиться в файлах shape.rb, circle.rb, rectangle.rb и triangle.rb. В main.rb содержится предоставленный код ввода/вывода, и его нельзя изменять.

Попробуйте сами

require_relative 'shape'
require_relative 'circle'
require_relative 'rectangle'
require_relative 'triangle'

# Предоставленный код ввода/вывода: оставьте его как есть
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 iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Ruby