نمط البناء
جزء من قسم البرمجة كائنية التوجه في رحلة R على Coddy. الدرس 47 من 57.
يقوم المنشئ بتجميع كائن معقد خطوةً بخطوة، وينشئه في النهاية باستخدام build(). يحتفظ المنشئ القائم على الإغلاق بالأجزاء في متغيرات خاصة؛ وتُعيد كل خطوة المنشئ نفسه باستخدام invisible(self)، بحيث تتسلسل الخطوات، كما أن استدعاء خطوة بمفردها لا يطبع شيئًا:
pizza_builder <- function() {
size <- "medium"
toppings <- character(0)
self <- list(
size = function(s) { size <<- s; invisible(self) },
add = function(t) { toppings <<- c(toppings, t); invisible(self) },
build = function() structure(list(size = size, toppings = toppings), class = "pizza")
)
self
}
p <- pizza_builder()$size("large")$add("olives")$add("basil")$build()
cat(p$size, "with", paste(p$toppings, collapse = " and "))المخرجات:
large with olives and basilbuild() هو المكان المناسب للتحقق من اكتمال الكائن. تتوقف الأجزاء المفقودة مع ظهور رسالة بدلًا من إنتاج كائن غير مكتمل:
email_builder <- function() {
to <- NULL
subject <- ""
self <- list(
to = function(x) { to <<- x; invisible(self) },
subject = function(x) { subject <<- x; invisible(self) },
build = function() {
if (is.null(to)) stop("an email needs a recipient", call. = FALSE)
paste0("To: ", to, " | Subject: ", subject)
}
)
self
}
print(email_builder()$to("ada@x.org")$subject("Hi")$build())
print(tryCatch(email_builder()$subject("Hi")$build(), error = function(e) conditionMessage(e)))الناتج:
[1] "To: ada@x.org | Subject: Hi"
[1] "an email needs a recipient"تعمل الفكرة نفسها مع القيم العادية ومع الأنبوب |>. كل خطوة هي دالة تأخذ الوصف حتى الآن وتُرجع نسخة محدَّثة منه، وتبني الخطوة الأخيرة النتيجة:
request <- function(url) list(url = url, method = "GET", headers = character(0))
with_method <- function(req, m) { req$method <- m; req }
with_header <- function(req, name, value) { req$headers[name] <- value; req }
build_request <- function(req) {
c(paste(req$method, req$url), paste0(names(req$headers), ": ", req$headers))
}
r <- request("/users") |> with_method("POST") |> with_header("Accept", "json") |> build_request()
cat(r, sep = "\n")الناتج:
POST /users
Accept: jsonيستبدل المُنشئُ مُنشئًا آخرَ يحتوي على العديد من الوسائط الاختيارية. تُسمّي كل خطوة ما تضبطه، وتحافظ الأجزاء التي لم يُذكرها على قيمها الافتراضية:
# صعب القراءة: ما هو TRUE، ما هو 3؟
make_report("sales", TRUE, FALSE, 3, "pdf")
# الباني يوضح معنى كل قيمة
report_builder("sales")$with_totals()$top(3)$format("pdf")$build()التحدي
سهلأكمل query_builder(table) القائم على الإغلاق في QueryBuilder.R. تُعيد خطواته المُنشئ بحيث يمكن تسلسلها:
select(cols): الأعمدة التي سيتم عرضها (الافتراضي*)؛where(cond): يضيف شرطًا؛ وتُضم عدة شروط باستخدامAND؛order_by(col)وlimit(n)؛build(): تُعيد الاستعلام، مثلSELECT name, age FROM users WHERE age > 30 AND city = 'Rome' ORDER BY name LIMIT 5، مع حذف الأجزاء التي لم يتم تعيينها.
يقرأ الكود المُقدَّم الجدول، ثم الأسطر select name age وwhere age > 30 وorder name أو limit 5، ويطبع الاستعلام المُنشأ. ثم ينشئ استعلامًا ثانيًا في سلسلة واحدة.
ضع الكود الخاص بك في QueryBuilder.R. يحتوي main.R على كود الإدخال/الإخراج المُقدَّم ولا يمكن تعديله.
جرّب بنفسك
source("QueryBuilder.R")
# كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
input <- suppressWarnings(readLines(file("stdin")))
q <- query_builder(input[1])
for (line in input[-1]) {
p <- strsplit(line, " ")[[1]]
rest <- paste(p[-1], collapse = " ")
if (p[1] == "select") q <- q$select(p[-1])
if (p[1] == "where") q <- q$where(rest)
if (p[1] == "order") q <- q$order_by(p[2])
if (p[1] == "limit") q <- q$limit(as.numeric(p[2]))
}
cat(q$build(), "\n", sep = "")
cat(query_builder("logs")$select("msg")$limit(1)$build(), "\n", sep = "")
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات S3
التعامل مع الملفاتالقوائم باستخدام فئةدوال المُنشئأساليب الطباعة والتنسيقأدوات التحقق والمساعدةمراجعة - درجات الحرارة2S3 الدوال العامة والطرق
الدوال العامة وUseMethodالطرق الافتراضيةطرق الدوال العامة الأساسيةأساسيات NextMethodمراجعة - مساحات الأشكالتدرّب بنفسك: مترجم R عبر الإنترنت