Méthodes abstraites
Fait partie de la section Programmation orientée objet du Journey Ruby de Coddy. Leçon 34 sur 57.
Une superclasse peut définir les étapes d’un algorithme et laisser certaines étapes à ses sous-classes. Il s’agit du modèle template method : render ci-dessous est fixe, tandis que header et row diffèrent selon le rapport :
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]])Sortie :
name,price
pen,2
cup,5
(CsvReport)La superclasse doit toujours indiquer quelles méthodes une sous-classe doit écrire. La convention consiste à utiliser une méthode abstraite qui déclenche NotImplementedError avec un message clair :
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
endSortie :
HtmlReport must implement rowNotImplementedError n’est pas une StandardError, donc un rescue nu ne l’intercepte pas. Cela empêche qu’une méthode manquante soit ignorée par la gestion générale des erreurs ; interceptez-la par son nom lorsque vous en avez besoin :
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)Sortie :
caught by name: missing step
falseÀ côté des méthodes abstraites, un modèle peut proposer des méthodes hook : des étapes avec un comportement par défaut que les sous-classes peuvent redéfinir. footer ci-dessus est un hook ; il en va de même pour une méthode oui/non telle que 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 : réponse par défaut
false
end
endDéfi
FacileComplétez les classes de rapport. Report#render(items) renvoie la ligne d’en-tête, une ligne row par élément et le footer, reliés par des sauts de ligne. header et row(item) sont abstraites : elles lèvent NotImplementedError avec HtmlReport must implement row (le nom de la classe et de la méthode). footer est un point d’extension qui renvoie end of report. CsvReport possède l’en-tête name,qty et des lignes pen,3. TextReport possède l’en-tête NAME QTY, des lignes avec le nom complété jusqu’à 8 caractères (pen 3), et le pied de page 2 items. HtmlReport ne définit que l’en-tête [table].
Le code fourni lit le type de rapport sur la première ligne et les éléments sous forme de lignes name qty, puis affiche render ou le message d’erreur.
Votre code doit être placé dans report.rb, csv_report.rb, text_report.rb et html_report.rb. main.rb contient le code fourni d’entrée/sortie et ne peut pas être modifié.
Essayez vous-même
require_relative 'report'
require_relative 'csv_report'
require_relative 'text_report'
require_relative 'html_report'
# Code d'entrée/sortie fourni : le garder tel quel
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
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation orientée objet
1Classes et objets
Travailler avec des fichiersDéfinir des classesVariables d’instanceMéthodes et selfto_s et inspectRécapitulatif - Carte de bibliothèque4Encapsulation
Méthodes privéesMéthodes protégéesSetters privésProtéger l’étatRécapitulatif - Distributeur automatique7Polymorphisme et typage canard
PolymorphismeTypage canardMéthodes abstraitesCas et classesRécapitulatif - méthodes de paiement10Patrons de conception – Partie 2
Délégation avec ForwardablePatron DécorateurPatron BuilderPatron État2Accesseurs et self
Lecteurs et écrivainsSetters personnalisésself dans les méthodesIdentité et égalitéRécapitulatif - Liste de contacts5Héritage
Sous-classesRedéfinition des méthodesAppeler superis_a? et ancêtresRécapitulatif - Paie du personnel8Méthodes spéciales
Égalité et hachageMéthodes d’opérateursIndexation et ajoutCollections personnaliséesRécapitulatif - Fractions11Projet : Gestion de bibliothèque
Livres et membresEmprunter des livresEntraînez-vous par vous-même : Compilateur Ruby en ligne