Métodos abstractos
Parte de la sección Programación orientada a objetos del Journey de Ruby de Coddy. Lección 34 de 57.
Una superclase puede definir los pasos de un algoritmo y dejar algunos pasos a sus subclases. Este es el patrón de método plantilla: render está fijado, mientras que header y row difieren según el informe:
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]])Salida:
name,price
pen,2
cup,5
(CsvReport)La superclase aún debe indicar qué métodos debe escribir una subclase. La convención consiste en usar un método abstracto que genere NotImplementedError con un mensaje claro:
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
endSalida:
HtmlReport must implement rowNotImplementedError no es un StandardError, por lo que un rescue independiente no lo captura. Esto evita que un método faltante sea absorbido por el manejo general de errores; rescátalo por nombre cuando necesites:
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)Salida:
caught by name: missing step
falseJunto a los métodos abstractos, una plantilla puede ofrecer métodos hook: pasos con un comportamiento predeterminado que las subclases pueden sobrescribir. footer arriba es un hook; también lo es un método de respuesta afirmativa o negativa como 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: respuesta por defecto
false
end
endDesafío
FácilCompleta las clases de informes. Report#render(items) devuelve la línea de encabezado, una row por elemento y el footer, unidos por saltos de línea. header y row(item) son abstractos: generan NotImplementedError con HtmlReport must implement row (el nombre de la clase y el método). footer es un hook que devuelve end of report. CsvReport tiene el encabezado name,qty y filas pen,3. TextReport tiene el encabezado NAME QTY, filas con el nombre rellenado hasta 8 caracteres (pen 3) y el pie 2 items. HtmlReport solo define el encabezado [table].
El código proporcionado lee el tipo de informe de la primera línea y los elementos como líneas con el formato name qty, después imprime render o el mensaje de error.
Tu código va en report.rb, csv_report.rb, text_report.rb y html_report.rb. main.rb contiene el código de entrada/salida proporcionado y no se puede editar.
Pruébalo tú mismo
require_relative 'report'
require_relative 'csv_report'
require_relative 'text_report'
require_relative 'html_report'
# Código de entrada/salida suministrado: déjalo tal como está
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
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Clases y objetos
Trabajar con archivosDefinir clasesVariables de instanciaMétodos y selfto_s e inspectRepaso: tarjeta de biblioteca4Encapsulación
Métodos privadosMétodos protegidosAsignadores privadosProtección del estadoRepaso: máquina expendedora7Polimorfismo y tipado pato
PolimorfismoTipado patoMétodos abstractoscase y clasesRepaso: métodos de pago2Accesores y self
Lectores y escritoresSetters personalizadosself en los métodosIdentidad e igualdadRepaso: lista de contactos5Herencia
SubclasesSobrescritura de métodosLlamar a superis_a? y ancestrosRepaso - Nómina del personal8Métodos especiales
Igualdad y hashMétodos de operadoresIndexación y adiciónColecciones personalizadasRepaso: fracciones11Proyecto: Gestión de biblioteca
Libros y miembrosPréstamo de librosPractica por tu cuenta: Compilador de Ruby online