שיטות מופשטות
חלק מהיחידה תכנות מונחה עצמים במסלול ה-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 שלמעלה היא hook; וכך גם מתודה של כן או לא כמו 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
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
3מתודות ומשתנים של מחלקה
מתודות של מחלקהמשתני מופע של מחלקהקבועיםמתודות ליצירת מופעיםסיכום – קודי קופוןתרגלו בעצמכם: קומפיילר Ruby אונליין