Абстрактные методы
Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 34 из 57.
Суперкласс может определить шаги алгоритма, оставив некоторые шаги своим подклассам. Это паттерн шаблонный метод: render ниже неизменен, а header и row различаются для каждого отчёта:
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]])Результат:
name,price
pen,2
cup,5
(CsvReport)Суперкласс по-прежнему должен указывать, какие методы подкласс обязан реализовать. Принято объявлять абстрактный метод, который вызывает NotImplementedError с понятным сообщением:
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Вывод:
HtmlReport must implement rowNotImplementedError не является подклассом StandardError, поэтому голый rescue его не перехватывает. Это не позволяет общей обработке ошибок поглотить отсутствие метода; если нужно, перехватите его по имени:
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)Вывод:
caught by name: missing step
falseПомимо абстрактных методов, шаблон может предоставлять методы hook: шаги со значением по умолчанию, которые подклассы могут переопределить. Приведённый выше footer — это такой метод; как и метод с ответом «да» или «нет», например 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? # хук: ответ по умолчанию
false
end
endЗадание
ЛегкоЗаверши классы отчётов. Report#render(items) возвращает строку заголовка, по одной row для каждого элемента и footer, объединённые символами новой строки. header и row(item) — абстрактные методы: они вызывают исключение NotImplementedError с сообщением HtmlReport must implement row (имя класса и метода). footer — это хук, который возвращает end of report. У CsvReport заголовок name,qty и строки pen,3. У TextReport заголовок NAME QTY, строки с именем, дополненным пробелами до 8 символов (pen 3), и нижний колонтитул 2 items. В HtmlReport определён только заголовок [table].
Предоставленный код считывает тип отчёта из первой строки, а элементы — из строк в формате name qty, затем выводит результат render или сообщение об ошибке.
Твой код нужно поместить в report.rb, csv_report.rb, text_report.rb и html_report.rb. В main.rb находится предоставленный код ввода/вывода, который нельзя редактировать.
Попробуйте сами
require_relative 'report'
require_relative 'csv_report'
require_relative 'text_report'
require_relative 'html_report'
# Предоставленный код ввода-вывода: оставьте его без изменений
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
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Классы и объекты
Работа с файламиОпределение классовПеременные экземпляраМетоды и selfto_s и inspectПовторение — читательский билет4Инкапсуляция
Приватные методыЗащищённые методыПриватные сеттерыЗащита состоянияПовторение — торговый автомат7Полиморфизм и утиная типизация
ПолиморфизмУтиная типизацияАбстрактные методыcase и классыПовторение — способы оплаты10Шаблоны проектирования, часть 2
Делегирование с помощью ForwardableШаблон декоратораШаблон строителяШаблон состояния2Аксессоры и self
Чтение и записьПользовательские сеттерыself в методахИдентичность и равенствоПовторение — список контактов5Наследование
ПодклассыПереопределение методовВызов superis_a? и предкиПовторение — расчёт зарплаты сотрудников8Специальные методы
Равенство и хешированиеМетоды операторовИндексация и добавлениеПользовательские коллекцииПовторение — дроби11Проект: Управление библиотекой
Книги и читателиВыдача книг3Методы и переменные классов
Методы классовПеременные экземпляра классаКонстантыФабричные методыПовторение — коды купонов6Модули и примеси
Модули и includeРасширение объектовМодуль ComparableМодуль EnumerableПовторение — спортивная лига9Паттерны проектирования. Часть 1
Паттерн «Одиночка»Фабричный паттернПаттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Онлайн-компилятор Ruby