Menu
Coddy logo textTech

Metodi astratti

Fa parte della sezione Programmazione orientata agli oggetti del percorso Ruby di Coddy. Lezione 34 di 57.

Una superclasse può definire i passaggi di un algoritmo e lasciarne alcuni alle sue sottoclassi. Questo è il pattern template method: render qui sotto è fisso, mentre header e row variano a seconda del report:

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

Output:

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

La superclasse dovrebbe comunque indicare quali metodi una sottoclasse deve implementare. La convenzione prevede un metodo astratto che genera NotImplementedError con un messaggio chiaro:

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

Output:

HtmlReport must implement row

NotImplementedError non è una StandardError, quindi un rescue senza argomenti non la intercetta. In questo modo, un metodo mancante non viene ignorato dalla gestione generale degli errori; intercettala per nome quando ne hai bisogno:

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)

Output:

caught by name: missing step
false

Accanto ai metodi astratti, un modello può offrire metodi hook: passaggi con un comportamento predefinito che le sottoclassi possono sovrascrivere. footer qui sopra è un hook; lo è anche un metodo sì o no come 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?     # punto de extensión: respuesta predeterminada
    false
  end
end
challenge icon

Sfida

Facile

Completa le classi dei report. Report#render(items) restituisce la riga di intestazione, una row per elemento e il footer, uniti da nuove righe. header e row(item) sono astratti: generano NotImplementedError con HtmlReport must implement row (il nome della classe e il metodo). footer è un hook che restituisce end of report. CsvReport ha l’intestazione name,qty e righe pen,3. TextReport ha l’intestazione NAME QTY, righe con il nome completato fino a 8 caratteri (pen 3) e il footer 2 items. HtmlReport definisce solo l’intestazione [table].

Il codice fornito legge il tipo di report dalla prima riga e gli elementi da righe name qty, quindi stampa render oppure il messaggio di errore.

Il tuo codice va inserito in report.rb, csv_report.rb, text_report.rb e html_report.rb. main.rb contiene il codice di input/output fornito e non può essere modificato.

Provalo tu

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

# Codice di input/output fornito: lascialo così com'è
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 iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Ruby online