Menu
CoddyTech

Method Abstrak

Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan Ruby di Coddy. Pelajaran 34 dari 57.

Superclass dapat mendefinisikan langkah-langkah algoritma dan menyerahkan beberapa langkah kepada subclass-nya. Ini adalah pola template method: render di bawah bersifat tetap, sedangkan header dan row berbeda untuk setiap laporan:

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)

Superclass tetap harus menyatakan method mana yang wajib ditulis oleh subclass. Konvensinya adalah method abstrak yang memunculkan NotImplementedError dengan pesan yang jelas:

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 bukan StandardError, jadi rescue tanpa argumen tidak akan menangkapnya. Ini mencegah method yang tidak ada tertutupi oleh penanganan error umum. Tangkap error ini berdasarkan namanya saat kamu membutuhkannya:

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

Selain method abstrak, template dapat menyediakan method hook: langkah dengan nilai bawaan yang dapat ditimpa oleh subclass. footer di atas adalah hook, begitu juga method ya atau tidak seperti 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: jawaban bawaan
    false
  end
end
challenge icon

Tantangan

Mudah

Lengkapi class laporan. Report#render(items) mengembalikan baris header, satu row untuk setiap item, dan footer, yang digabungkan dengan baris baru. header dan row(item) bersifat abstrak: keduanya memunculkan NotImplementedError dengan HtmlReport must implement row (nama class dan method). footer adalah hook yang mengembalikan end of report. CsvReport memiliki header name,qty dan baris pen,3. TextReport memiliki header NAME QTY, baris dengan nama yang diberi padding hingga 8 karakter (pen 3), dan footer 2 items. HtmlReport hanya mendefinisikan header [table].

Kode yang disediakan membaca jenis laporan dari baris pertama dan item sebagai baris name qty, lalu mencetak render atau pesan error.

Tulis kodemu di report.rb, csv_report.rb, text_report.rb, dan html_report.rb. main.rb berisi kode input/output yang disediakan dan tidak boleh diedit.

Coba sendiri

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

# Kode input/output yang disediakan: biarkan apa adanya
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 iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Objek

Latihan sendiri: Compiler Ruby online