Polimorfizm
Część sekcji Programowanie obiektowe ścieżki Ruby w Coddy. Lekcja 32 z 57.
Polimorfizm oznacza, że jedno wywołanie metody działa na obiektach różnych klas, a każdy obiekt odpowiada na swój sposób. Kod, który iteruje po kształtach, wywołuje na każdym z nich area, nie pytając, jaki to rodzaj kształtu:
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))Wynik:
circle 3.14
square 9.00
total 12.14Bez polimorfizmu wywołujący musi sprawdzać rodzaj każdego obiektu i powtarzać to sprawdzenie wszędzie tam, gdzie różnica ma znaczenie. Dzięki niemu różnica jest ukryta wewnątrz każdej klasy:
# bez polimorfizmu
case shape.kind
when "circle" then 3.14159 * shape.r ** 2
when "square" then shape.side ** 2
end
# z polimorfizmem
shape.areaDodanie nowego rodzaju obiektu nie wymaga więc zmiany kodu wywołującego: nowa podklasa z własną metodą area działa w każdej istniejącej już pętli:
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 }Wynik:
area 4
area 6to_s to typowy przykład: puts wywołuje tę metodę na dowolnym obiekcie, a każda klasa odpowiada inaczej. Twoje klasy również mogą w tym uczestniczyć, definiując własną metodę to_s:
class Coin
def to_s
"a coin"
end
end
[42, "text", nil, [1, 2], Coin.new].each { |x| puts "[#{x}]" }Wynik:
[42]
[text]
[]
[[1, 2]]
[a coin]Wyzwanie
ŁatwyUzupełnij definicje figur. Shape#to_s zwraca circle: 3.14, nazwę klasy zapisaną małymi literami oraz pole powierzchni z dwoma miejscami po przecinku, a każda podklasa udostępnia area: Circle.new(r) (użyj 3.14159 jako wartości pi), Rectangle.new(w, h) i Triangle.new(base, height).
Dostarczony kod odczytuje wiersze circle 1, rectangle 2 3 lub triangle 4 5, wyświetla każdą figurę, a następnie total: 22.14 i largest: rectangle: 6.00, korzystając wyłącznie z to_s i area.
Umieść swój kod w plikach shape.rb, circle.rb, rectangle.rb i triangle.rb. Plik main.rb zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
require_relative 'shape'
require_relative 'circle'
require_relative 'rectangle'
require_relative 'triangle'
# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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)}"
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Klasy i obiekty
Praca z plikamiDefiniowanie klasZmienne instancjiMetody i selfto_s i inspectPowtórzenie – karta biblioteczna4Hermetyzacja
Metody prywatneMetody chronionePrywatne setteryOchrona stanuPowtórzenie – automat vendingowy7Polimorfizm i typowanie kacze
PolimorfizmTypowanie kaczeMetody abstrakcyjnecase i klasyPowtórzenie – metody płatności10Wzorce projektowe, część 2
Delegowanie z ForwardableWzorzec dekoratoraWzorzec budowniczegoWzorzec stanu2Metody dostępowe i self
Gettery i setteryWłasne setteryself w metodachTożsamość i równośćPowtórzenie – lista kontaktów5Dziedziczenie
PodklasyNadpisywanie metodWywoływanie superis_a? i przodkowiePowtórka – lista płac pracowników8Metody specjalne
Równość i hashMetody operatorówIndeksowanie i dodawanieWłasne kolekcjePowtórka – ułamki11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książekPoćwicz samodzielnie: Kompilator Ruby online