Menu
Coddy logo textTech

Polymorphisme

Fait partie de la section Programmation orientée objet du Journey Ruby de Coddy. Leçon 32 sur 57.

Le polymorphisme signifie qu’un même appel de méthode fonctionne sur des objets de classes différentes, et que chaque objet répond à sa manière. Le code qui parcourt des formes appelle area sur chacune d’elles sans demander de quel type de forme il s’agit :

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

Sortie :

circle 3.14
square 9.00
total 12.14

Sans polymorphisme, l’appelant doit vérifier le type de chaque objet et répéter cette vérification partout où la différence importe. Avec le polymorphisme, la différence réside dans chaque classe :

# sans polymorphisme
case shape.kind
when "circle" then 3.14159 * shape.r ** 2
when "square" then shape.side ** 2
end

# avec polymorphisme
shape.area

Ajouter un nouveau type d’objet ne nécessite donc aucune modification du code appelant : une nouvelle sous-classe avec sa propre méthode area fonctionne dans toute boucle déjà existante :

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 }

Sortie :

area 4
area 6

to_s est l’exemple courant : puts l’appelle sur n’importe quel objet, et chaque classe y répond différemment. Vos classes participent elles aussi en définissant leur propre to_s :

class Coin
  def to_s
    "a coin"
  end
end

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

Sortie :

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

Défi

Facile

Complétez les formes. Shape#to_s renvoie circle: 3.14, le nom de la classe en minuscules et l’aire avec deux décimales, et chaque sous-classe fournit area : Circle.new(r) (utilisez 3.14159 pour pi), Rectangle.new(w, h) et Triangle.new(base, height).

Le code fourni lit les lignes circle 1, rectangle 2 3 ou triangle 4 5, affiche chaque forme, puis total: 22.14 et largest: rectangle: 6.00, en utilisant uniquement to_s et area.

Votre code doit se trouver dans shape.rb, circle.rb, rectangle.rb et triangle.rb. main.rb contient le code d’entrée/sortie fourni et ne peut pas être modifié.

Essayez vous-même

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

# Code d'entrée/sortie fourni : le garder tel quel
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 iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur Ruby en ligne