Menu
CoddyTech

Abstrakte Methoden

Teil des Abschnitts Objektorientierte Programmierung der Ruby-Journey von Coddy. Lektion 34 von 57.

Eine Oberklasse kann die Schritte eines Algorithmus definieren und einige Schritte ihren Unterklassen überlassen. Das ist das Template-Method-Muster: render ist unten festgelegt, während header und row je nach Bericht unterschiedlich sind:

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

Ausgabe:

name,price
pen,2
cup,5
(CsvReport)

Die Oberklasse sollte weiterhin angeben, welche Methoden eine Unterklasse schreiben muss. Die Konvention ist eine abstrakte Methode, die NotImplementedError mit einer klaren Meldung auslöst:

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
end

Ausgabe:

HtmlReport must implement row

NotImplementedError ist kein StandardError, daher fängt ein bloßes rescue ihn nicht ab. Dadurch wird verhindert, dass eine fehlende Methode von der allgemeinen Fehlerbehandlung verschluckt wird; fange ihn bei Bedarf namentlich ab:

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)

Ausgabe:

caught by name: missing step
false

Zusätzlich zu abstrakten Methoden kann ein Template Hook-Methoden anbieten: Schritte mit einem Standardwert, den Unterklassen überschreiben können. footer oben ist ein Hook; ebenso eine Ja-Nein-Methode wie 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?     # Hook: Standardantwort
    false
  end
end
challenge icon

Aufgabe

Einfach

Vervollständige die Report-Klassen. Report#render(items) gibt die Kopfzeile, eine row pro Element und den footer zurück, durch Zeilenumbrüche verbunden. header und row(item) sind abstrakt: Sie lösen NotImplementedError mit HtmlReport must implement row aus (dem Klassennamen und der Methode). footer ist ein Hook, der end of report zurückgibt. CsvReport hat die Kopfzeile name,qty und Zeilen mit pen,3. TextReport hat die Kopfzeile NAME QTY, Zeilen, in denen der Name auf 8 Zeichen aufgefüllt ist (pen 3), und den Footer 2 items. HtmlReport definiert nur die Kopfzeile [table].

Der bereitgestellte Code liest die Report-Art aus der ersten Zeile und die Elemente aus Zeilen im Format name qty und gibt dann render oder die Fehlermeldung aus.

Dein Code gehört in report.rb, csv_report.rb, text_report.rb und html_report.rb. main.rb enthält den bereitgestellten Ein-/Ausgabecode und darf nicht bearbeitet werden.

Probier es selbst

require_relative 'report'
require_relative 'csv_report'
require_relative 'text_report'
require_relative 'html_report'

# Vorgegebener Ein-/Ausgabecode: unverändert lassen
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
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Ruby-Compiler