Menu
CoddyTech

Soyut Metotlar

Coddy'nin Ruby Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 34 / 57.

Bir üst sınıf, bir algoritmanın adımlarını tanımlayıp bazı adımları alt sınıflarına bırakabilir. Bu, şablon metot desenidir: aşağıdaki render sabittir, header ve row ise her rapora göre değişir:

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

Çıktı:

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

Üst sınıf, bir alt sınıfın hangi yöntemleri yazması gerektiğini yine belirtmelidir. Gelenek, açık bir mesajla NotImplementedError yükselten bir soyut yöntem kullanmaktır:

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

Çıktı:

HtmlReport must implement row

NotImplementedError, StandardError değildir; bu yüzden yalın bir rescue onu yakalamaz. Böylece eksik bir metodun genel hata işleme tarafından yutulması önlenir; gerektiğinde adıyla yakalayın:

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)

Çıktı:

caught by name: missing step
false

Soyut metotların yanı sıra bir şablon, kanca metotları da sunabilir: alt sınıfların üzerine yazabileceği varsayılan davranışa sahip adımlar. Yukarıdaki footer bir kancadır; include_total? gibi evet-hayır döndüren bir metot da öyledir:

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: varsayılan yanıt
    false
  end
end
challenge icon

Görev

Kolay

Rapor sınıflarını tamamla. Report#render(items), başlık satırını, her öğe için bir row satırını ve footer değerini yeni satırlarla birleştirerek döndürür. header ve row(item) soyuttur: NotImplementedError hatasını HtmlReport must implement row (sınıf adı ve yöntem) ile yükseltirler. footer, end of report döndüren bir kancadır. CsvReport başlık olarak name,qty değerine ve satır olarak pen,3 değerine sahiptir. TextReport başlık olarak NAME QTY değerine, adı 8 karaktere tamamlanmış satırlara (pen 3) ve altbilgi olarak 2 items değerine sahiptir. HtmlReport yalnızca başlığı [table] olarak tanımlar.

Sağlanan kod, rapor türünü ilk satırdan, öğeleri ise name qty satırlarından okur; ardından render sonucunu veya hata mesajını yazdırır.

Kodunu report.rb, csv_report.rb, text_report.rb ve html_report.rb dosyalarına yaz. main.rb, sağlanan girdi/çıktı kodunu içerir ve düzenlenemez.

Kendin dene

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

# Sağlanan giriş/çıkış kodu: olduğu gibi bırakın
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 iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler

Kendi başına pratik yap: Online Ruby derleyicisi