Menu
Coddy logo textTech

תבנית Singleton

חלק מהיחידה תכנות מונחה עצמים במסלול ה-R של Coddy. שיעור 42 מתוך 57.

תבנית עיצוב היא פתרון מוכח לבעיית עיצוב שחוזרת שוב ושוב. תבנית singleton מוודאת שיש בדיוק מופע אחד של דבר כלשהו, שאפשר לגשת אליו מכל מקום. ב-R, פונקציית גישה יוצרת את המופע בקריאה הראשונה אליה, שומרת אותו במשתנה שנוצר באמצעות local(), ומחזירה את אותו מופע בכל פעם:

get_config <- local({
  instance <- NULL
  function() {
    if (is.null(instance)) {
      instance <<- new.env()
      instance$theme <- "light"
    }
    instance
  }
})
a <- get_config()
a$theme <- "dark"
b <- get_config()
cat(b$theme, identical(a, b))

פלט:

dark TRUE

המופע הוא סביבה, לכן שינוי שנעשה באמצעות קריאה אחת גלוי לכל חלק אחר של התוכנית. שתי פונקציות במקומות שונים כותבות לאותו יומן:

get_log <- local({
  instance <- NULL
  function() {
    if (is.null(instance)) {
      instance <<- new.env()
      instance$lines <- character(0)
    }
    instance
  }
})
page_a <- function() {
  log <- get_log()
  log$lines <- c(log$lines, "a visited")
}
page_b <- function() {
  log <- get_log()
  log$lines <- c(log$lines, "b visited")
}
page_a()
page_b()
page_a()
print(get_log()$lines)

פלט:

[1] "a visited" "b visited" "a visited"

המופע נוצר רק כאשר הוא נדרש לראשונה. ספירת היצירות מראה שזה קורה פעם אחת, בלי קשר למספר הפעמים שבהן נקראת פונקציית הגישה:

created <- 0
get_db <- local({
  instance <- NULL
  function() {
    if (is.null(instance)) {
      created <<- created + 1
      instance <<- new.env()
    }
    instance
  }
})
for (i in 1:5) db <- get_db()
cat("created", created, "time(s)")

פלט:

created 1 time(s)

השתמשו בסינגלטונים במידה מועטה. סינגלטון הוא מצב גלובלי: כל קוד יכול לשנות אותו, ובדיקה לא יכולה להתחיל מעותק נקי בלי לאפס אותו. הוא מתאים לדברים שיש מהם באמת רק אחד בכל התוכנית, כמו מתעד או ההגדרות; לעיתים קרובות, העברת אובייקט כארגומנט ברורה יותר:

# קל יותר לבדוק: ה-log מועבר כארגומנט
process <- function(order, log) {
  log$lines <- c(log$lines, paste("processing", order$id))
}
challenge icon

אתגר

קל

השלם את הלוגר היחיד ב־Logger.R. הפונקציה get_logger() מחזירה סביבה אחת, שנוצרת בקריאה הראשונה, עם השדה entries (מחרוזת, ריקה בהתחלה). לאחר מכן כתוב:

  • את log_info(msg) ואת log_error(msg), שמוסיפות את [INFO] msg או את [ERROR] msg לרשומות של הלוגר המשותף;
  • את error_count(), שסופרת את הרשומות שמתחילות ב־[ERROR].

הקוד שסופק קורא שורות info,started או error,disk full, רושם כל אחת מהן, ואז מדפיס כל רשומה, את מספר השגיאות, והאם שתי קריאות ל־get_logger() מחזירות את אותו אובייקט.

הקוד שלך יופיע ב־Logger.R. הקובץ main.R מכיל את קוד הקלט/פלט שסופק ואי אפשר לערוך אותו.

נסו בעצמכם

source("Logger.R")

# קוד הקלט/פלט שסופק: השאירו אותו כפי שהוא
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, ",")[[1]]
  if (p[1] == "error") log_error(p[2]) else log_info(p[2])
}
for (e in get_logger()$entries) cat(e, "\n", sep = "")
cat("errors: ", error_count(), "\n", sep = "")
cat("one logger: ", identical(get_logger(), get_logger()), "\n", sep = "")
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר R אונליין