תבנית Singleton
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Ruby של Coddy. שיעור 42 מתוך 57.
תבנית עיצוב היא פתרון מוכח לבעיית עיצוב נפוצה. תבנית singleton מבטיחה שלמחלקה יש בדיוק מופע אחד, שניתן לגשת אליו מכל מקום. ספריית התקן של Ruby כוללת אותה: require 'singleton' ו-include Singleton מוסיפים למחלקה מתודה instance:
require 'singleton'
class Settings
include Singleton
attr_accessor :theme
def initialize
@theme = "light"
end
end
Settings.instance.theme = "dark"
puts Settings.instance.theme
puts Settings.instance.equal?(Settings.instance)פלט:
dark
trueinclude Singleton גם הופך את new לפרטית, כך שלא ניתן ליצור מופע שני. כל חלק בתוכנית שמבקש את instance מקבל את אותו אובייקט:
require 'singleton'
class Counter
include Singleton
attr_reader :hits
def initialize
@hits = 0
end
def hit
@hits += 1
end
end
def page_a
Counter.instance.hit
end
def page_b
2.times { Counter.instance.hit }
end
page_a
page_b
puts Counter.instance.hits
begin
Counter.new
rescue NoMethodError => e
puts e.message
endפלט:
3
private method `new' called for class Counterאותו רעיון, כשהוא נכתב ידנית, הוא מתודת מחלקה שיוצרת את המופע בשימוש הראשון ומתודת new פרטית. כך אפשר לראות מה המודול עושה עבורך:
class Config
def self.instance
@instance ||= new
end
private_class_method :new
attr_accessor :level
def initialize
@level = 1
end
end
Config.instance.level = 3
puts Config.instance.levelפלט:
3השתמשו ביחידות מופע יחיד במשורה. יחידת מופע יחיד היא מצב גלובלי: כל קוד יכול לשנות אותו, ובדיקות לא יכולות להתחיל מעותק נקי. הן מתאימות לדברים שבאמת קיימים פעם אחת בכל תוכנית, כמו יומן או הגדרות התצורה; לעיתים קרובות ברור יותר להעביר אובייקט כפרמטר:
# קל יותר לבדוק: ה-logger מועבר כפרמטר
def process(order, log)
log.write("processing #{order.id}")
endאתגר
קלהשלימו את הסינגלטון AppLogger באמצעות include Singleton. הוא מחזיק מערך של רשומות ויש לו את log(message, level), ששומר [INFO] message או [ERROR] message (הרמה באותיות גדולות), את entries ואת error_count. פונקציות העזר handle_info(message) ו־handle_error(message) שב־functions.rb רושמות שתיהן דרך AppLogger.instance, ולכן הן מגיעות לאותו אובייקט.
הקוד שסופק קורא שורות מסוג info,message או error,message, שולח אותן דרך פונקציות העזר, מדפיס כל רשומה ואת errors: 1, ובודק אם הקריאה ל־AppLogger.new נדחית.
הקוד שלכם ייכתב בקבצים app_logger.rb ו־functions.rb. הקובץ main.rb מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.
נסו בעצמכם
require_relative 'app_logger'
require_relative 'functions'
# קוד קלט/פלט שסופק: יש להשאיר אותו כפי שהוא
input = []
while (line = gets)
input << line.chomp
end
input.each do |line|
level, message = line.split(",", 2)
level == "error" ? handle_error(message) : handle_info(message)
end
AppLogger.instance.entries.each { |e| puts e }
puts "errors: #{AppLogger.instance.error_count}"
begin
AppLogger.new
puts "new is allowed"
rescue NoMethodError
puts "new is private"
end
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
3מתודות ומשתנים של מחלקה
מתודות של מחלקהמשתני מופע של מחלקהקבועיםמתודות ליצירת מופעיםסיכום – קודי קופוןתרגלו בעצמכם: קומפיילר Ruby אונליין