نمط Factory
جزء من قسم البرمجة كائنية التوجه في رحلة R على Coddy. الدرس 43 من 57.
المصنع هو دالة تنشئ كائنات، بحيث لا يحتاج المستدعي إلى معرفة الفئة أو مُنشئها. وهي تأخذ وصفًا، مثل اسم نوع، وتُرجع الكائن المناسب:
area <- function(s) UseMethod("area")
area.circle <- function(s) pi * s$r^2
area.square <- function(s) s$side^2
make_shape <- function(type, size) {
switch(type,
circle = structure(list(r = size), class = "circle"),
square = structure(list(side = size), class = "square"))
}
for (t in c("circle", "square")) cat(t, round(area(make_shape(t, 2)), 2), "\n")الناتج:
circle 12.57
square 4 بالنسبة إلى نوع لا يعرفه، تُرجع switch() القيمة NULL. ينبغي أن تتوقف المُنشئة برسالة تُسمّي النوع بدلًا من ذلك، لكي يظهر الخطأ في المكان الذي حدث فيه:
make_shape <- function(type, size) {
switch(type,
circle = structure(list(r = size), class = "circle"),
square = structure(list(side = size), class = "square"),
stop("unknown shape: ", type, call. = FALSE))
}
msg <- tryCatch(make_shape("hexagon", 1), error = function(e) conditionMessage(e))
cat(msg)الناتج:
unknown shape: hexagonتحتفظ سجلّ بالمنشئات في قائمة مُسمّاة، لذا لا يتغير المصنع نفسه أبدًا. هنا، السجل هو بيئة، بينما تضيف register_shape() نوعًا جديدًا أثناء تشغيل البرنامج:
registry <- new.env()
register_shape <- function(name, maker) assign(name, maker, envir = registry)
make_shape <- function(name, ...) {
if (!exists(name, envir = registry, inherits = FALSE)) stop("unknown shape: ", name, call. = FALSE)
get(name, envir = registry)(...)
}
register_shape("circle", function(r) structure(list(r = r), class = "circle"))
register_shape("rect", function(w, h) structure(list(w = w, h = h), class = "rect"))
print(class(make_shape("rect", 2, 3)))
print(sort(ls(registry)))الناتج:
[1] "rect"
[1] "circle" "rect" يعتمد الكود الذي يستخدم الكائنات فقط على الدوال العامة، مثل area(). وتعمل فئة جديدة مسجَّلة ولديها طريقة area() مع ذلك الكود من دون إجراء أي تغيير عليه:
registry <- new.env()
register_shape <- function(name, maker) assign(name, maker, envir = registry)
make_shape <- function(name, ...) get(name, envir = registry)(...)
area <- function(s) UseMethod("area")
total_area <- function(shapes) sum(vapply(shapes, area, numeric(1)))
register_shape("square", function(a) structure(list(a = a), class = "square"))
area.square <- function(s) s$a^2
register_shape("tri", function(b, h) structure(list(b = b, h = h), class = "tri"))
area.tri <- function(s) s$b * s$h / 2
print(total_area(list(make_shape("square", 2), make_shape("tri", 4, 3))))الناتج:
[1] 10التحدي
سهليمكن تصدير سجل بعدة تنسيقات. يحتوي Exporters.R على الدالة العامة export(exporter, record) والطريقة الخاصة بـ csv_exporter، التي تُرجع القيم مفصولة بفواصل. أكمل الطريقتين الأخريين هناك:
json_exporter:{"name": "Ada", "age": "36"}؛text_exporter:name=Ada; age=36.
ثم اكتب المُنشئ make_exporter(format) في Factory.R: تؤدي التنسيقات csv أو json أو text إلى قائمة فارغة ذات الصنف csv_exporter أو json_exporter أو text_exporter، وأي تنسيق آخر يتوقف مع unknown format: xml (استخدم call. = FALSE). يقرأ الكود المُزوَّد أسماء الحقول والقيم، ثم أسماء التنسيقات، ويطبع كل عملية تصدير أو error: والرسالة.
ضع الكود في Exporters.R وFactory.R. يحتوي main.R على كود الإدخال/الإخراج المُزوَّد ولا يمكن تعديله.
جرّب بنفسك
source("Exporters.R")
source("Factory.R")
# كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
input <- suppressWarnings(readLines(file("stdin")))
fields <- strsplit(input[1], ",")[[1]]
values <- strsplit(input[2], ",")[[1]]
record <- setNames(values, fields)
for (fmt in input[-(1:2)]) {
out <- tryCatch(export(make_exporter(fmt), record), error = function(e) paste("error:", conditionMessage(e)))
cat(out, "\n", sep = "")
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات S3
التعامل مع الملفاتالقوائم باستخدام فئةدوال المُنشئأساليب الطباعة والتنسيقأدوات التحقق والمساعدةمراجعة - درجات الحرارة2S3 الدوال العامة والطرق
الدوال العامة وUseMethodالطرق الافتراضيةطرق الدوال العامة الأساسيةأساسيات NextMethodمراجعة - مساحات الأشكالتدرّب بنفسك: مترجم R عبر الإنترنت