Metody abstrakcyjne
Część sekcji Programowanie obiektowe ścieżki Ruby w Coddy. Lekcja 34 z 57.
Klasa nadrzędna może określać kroki algorytmu i pozostawiać niektóre z nich swoim klasom podrzędnym. To wzorzec metody szablonowej: poniższa metoda render jest stała, natomiast header i row różnią się w zależności od raportu:
class Report
def render(items)
[header, *items.map { |i| row(i) }, footer].join("\n")
end
def footer
"(#{self.class.name})"
end
end
class CsvReport < Report
def header
"name,price"
end
def row(item)
item.join(",")
end
end
puts CsvReport.new.render([["pen", 2], ["cup", 5]])Wynik:
name,price
pen,2
cup,5
(CsvReport)Nadklasa nadal powinna wskazywać, które metody podklasa musi napisać. Konwencją jest metoda abstrakcyjna, która zgłasza NotImplementedError z jasnym komunikatem:
class Report
def render(items)
[header, *items.map { |i| row(i) }].join("\n")
end
def header
raise NotImplementedError, "#{self.class} must implement header"
end
def row(_item)
raise NotImplementedError, "#{self.class} must implement row"
end
end
class HtmlReport < Report
def header
"[table]"
end
end
begin
puts HtmlReport.new.render([1])
rescue NotImplementedError => e
puts e.message
endWynik:
HtmlReport must implement rowNotImplementedError nie jest błędem typu StandardError, więc samo rescue go nie przechwytuje. Dzięki temu brakująca metoda nie zostanie pominięta przez ogólną obsługę błędów; w razie potrzeby przechwyć ten błąd, podając jego nazwę:
def risky
raise NotImplementedError, "missing step"
end
begin
begin
risky
rescue => e
puts "general handler: #{e.message}"
end
rescue NotImplementedError => e
puts "caught by name: #{e.message}"
end
puts NotImplementedError.ancestors.include?(StandardError)Wynik:
caught by name: missing step
falseOprócz metod abstrakcyjnych szablon może udostępniać metody hakowe: kroki z domyślną implementacją, które podklasy mogą nadpisywać. Powyższe footer jest metodą hakową; podobnie jak metoda zwracająca odpowiedź tak lub nie, taka jak include_total?:
class Report
def render(items)
lines = [header, *items.map { |i| row(i) }]
lines << total_line(items) if include_total?
lines.join("\n")
end
def include_total? # punkt rozszerzenia: domyślna odpowiedź
false
end
endWyzwanie
ŁatwyUzupełnij klasy raportów. Report#render(items) zwraca wiersz nagłówka, jeden row dla każdego elementu oraz footer, połączone znakami nowej linii. header i row(item) są abstrakcyjne: zgłaszają NotImplementedError z HtmlReport must implement row (nazwą klasy i metody). footer to metoda haka, która zwraca end of report. CsvReport ma nagłówek name,qty i wiersze pen,3. TextReport ma nagłówek NAME QTY, wiersze z nazwą dopełnioną spacjami do 8 znaków (pen 3) oraz stopkę 2 items. HtmlReport definiuje tylko nagłówek [table].
Dostarczony kod odczytuje rodzaj raportu z pierwszego wiersza, a elementy z wierszy w formacie name qty, a następnie wyświetla wynik render lub komunikat o błędzie.
Twój kod umieść w plikach report.rb, csv_report.rb, text_report.rb i html_report.rb. main.rb zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
require_relative 'report'
require_relative 'csv_report'
require_relative 'text_report'
require_relative 'html_report'
# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
input = []
while (line = gets)
input << line.chomp
end
kinds = { "csv" => CsvReport, "text" => TextReport, "html" => HtmlReport }
report = kinds[input[0]].new
items = input.drop(1).map(&:split)
begin
puts report.render(items)
rescue NotImplementedError => e
puts e.message
end
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