نمط 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))
}التحدي
سهلأكمل مسجّل singleton في 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 = "")
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات S3
التعامل مع الملفاتالقوائم باستخدام فئةدوال المُنشئأساليب الطباعة والتنسيقأدوات التحقق والمساعدةمراجعة - درجات الحرارة2S3 الدوال العامة والطرق
الدوال العامة وUseMethodالطرق الافتراضيةطرق الدوال العامة الأساسيةأساسيات NextMethodمراجعة - مساحات الأشكالتدرّب بنفسك: مترجم R عبر الإنترنت