תבנית Singleton
חלק מהיחידה תכנות מונחה עצמים במסלול ה-Swift של Coddy. שיעור 42 מתוך 57.
תבנית עיצוב היא פתרון מוכח לבעיית עיצוב נפוצה. תבנית singleton מבטיחה שלמחלקה יהיה מופע אחד בדיוק, שניתן לגשת אליו מכל מקום. ב-Swift היא כוללת static let שמכיל את המופע, וכן private init כדי שאף אחד לא יוכל ליצור מופע נוסף:
final class Settings {
static let shared = Settings()
var theme = "light"
private init() {}
}
Settings.shared.theme = "dark"
print(Settings.shared.theme)פלט:
darkכל שימוש ב-Settings.shared מפנה לאותו אובייקט, ולכן שינוי שנעשה בחלק אחד של התוכנית גלוי בכל מקום. static let נוצר באופן עצלני בשימוש הראשון, ובאופן בטוח גם כאשר כמה תהליכונים מתחילים בו-זמנית:
final class Counter {
static let shared = Counter()
private(set) var hits = 0
private init() {}
func hit() { hits += 1 }
}
func pageA() { Counter.shared.hit() }
func pageB() { Counter.shared.hit(); Counter.shared.hit() }
pageA()
pageB()
print(Counter.shared.hits)
print(Counter.shared === Counter.shared)פלט:
3
trueמכיוון שהאתחול פרטי, Settings() לא מתקמפל מחוץ למחלקה, וכך נשמרת ההבטחה ל״אחד בלבד״:
// let other = Settings() // שגיאה: האתחול של 'Settings' אינו נגיש בגלל רמת ההגנה 'private'השתמשו ביחידונים במשורה. יחידון הוא מצב גלובלי: כל קוד יכול לשנות אותו, ובדיקות לא יכולות להתחיל בקלות מעותק נקי. הם מתאימים לדברים שיש מהם באמת רק אחד בכל התוכנית, כמו רשם או ההגדרות של האפליקציה; לרוב ברור יותר להעביר אובייקט כפרמטר.
// קל יותר לבדוק: ה־logger מועבר כפרמטר
func process(order: Order, log: Logger) { log.write("processing \(order.id)") }אתגר
קלהשלם את ה־singleton Logger. יש לו מופע static let shared, אתחול פרטי, מערך private(set) של entries, ואת log(_:level:), ששומר את [INFO] message או את [ERROR] message (הרמה היא info או error). errorCount היא מאפיין מחושב. הקוד שסופק קורא ל־Logger.shared משתי פונקציות שונות, לכן שתיהן חייבות להגיע לאותו מופע.
הקוד שסופק קורא שורות info,message או error,message, מעביר שורות מידע דרך handleInfo ושורות שגיאה דרך handleError, ואז מדפיס כל רשומה ואת errors: 1.
הקוד שלך נכנס לקובצי Logger.swift ו־Functions.swift. הקובץ main.swift מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.
נסו בעצמכם
// קוד הקלט/פלט שסופק: יש להשאיר אותו כפי שהוא
var input: [String] = []
while let line = readLine() { input.append(line) }
for line in input {
let p = line.split(separator: ",", maxSplits: 1).map(String.init)
if p[0] == "error" { handleError(p[1]) } else { handleInfo(p[1]) }
}
for e in Logger.shared.entries { print(e) }
print("errors: \(Logger.shared.errorCount)")
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
תרגלו בעצמכם: Swift אונליין