نمط Observer
جزء من قسم البرمجة كائنية التوجه في رحلة R على Coddy. الدرس 44 من 57.
يتيح نمط المراقب لكائن، هو الموضوع، إخبار الشيفرة الأخرى عند حدوث شيء ما دون معرفة من يستمع. يحتفظ الموضوع بقائمة من دوال ردّ الاتصال، وهي المراقبون، ويستدعي كلًّا منها عند كل حدث:
make_subject <- function() {
observers <- list()
list(
subscribe = function(f) observers[[length(observers) + 1]] <<- f,
notify = function(event) for (f in observers) f(event)
)
}
button <- make_subject()
button$subscribe(function(e) cat("logger saw", e, "\n"))
button$subscribe(function(e) cat("counter saw", e, "\n"))
button$notify("click")الناتج:
logger saw click
counter saw click يمكن أن يكون المراقب إغلاقًا له حالته الخاصة. هذا المراقب يحسب الأحداث التي رآها، بينما لا يعرف الكائن موضوع المراقبة شيئًا عن عملية العد:
make_subject <- function() {
observers <- list()
list(subscribe = function(f) observers[[length(observers) + 1]] <<- f,
notify = function(event) for (f in observers) f(event))
}
make_counter <- function() {
n <- 0
list(observe = function(event) n <<- n + 1, count = function() n)
}
door <- make_subject()
c1 <- make_counter()
door$subscribe(c1$observe)
for (e in c("open", "close", "open")) door$notify(e)
print(c1$count())الناتج:
[1] 3يتيح تخزين المُراقبين تحت أسماء لهم إلغاء الاشتراك. يؤدي observers[[name]] <<- NULL إلى إزالة أحدهم، بينما يستمر الآخرون في تلقي الأحداث:
make_subject <- function() {
observers <- list()
list(
subscribe = function(name, f) observers[[name]] <<- f,
unsubscribe = function(name) observers[[name]] <<- NULL,
notify = function(event) for (f in observers) f(event)
)
}
s <- make_subject()
s$subscribe("ada", function(e) cat("ada:", e, "\n"))
s$subscribe("bo", function(e) cat("bo:", e, "\n"))
s$notify("first")
s$unsubscribe("ada")
s$notify("second")الناتج:
ada: first
bo: first
bo: second يحدد الموضوع البيانات التي يحملها الحدث. يمرر مؤشّر الأسعار السعر القديم والسعر الجديد، ويُخطِر فقط عندما يتغير السعر فعليًا:
make_ticker <- function(price) {
observers <- list()
list(
subscribe = function(f) observers[[length(observers) + 1]] <<- f,
set_price = function(new) {
if (new == price) return(invisible(NULL))
old <- price
price <<- new
for (f in observers) f(old, new)
}
)
}
t <- make_ticker(10)
t$subscribe(function(old, new) cat("price", old, "->", new, "\n"))
for (p in c(12, 12, 9)) t$set_price(p)الناتج:
price 10 -> 12
price 12 -> 9 التحدي
سهلأكمل دالة الإنشاء make_newsletter() في Newsletter.R. فهي تحتفظ باستدعاءات القرّاء في قائمة خاصة مُسمّاة، وتُرجع ثلاث دوال:
- تخزّن
subscribe(name, f)الاستدعاء تحت الاسم (ويؤدي الاشتراك مرة أخرى إلى استبداله)؛ - تزيل
unsubscribe(name)الاستدعاء؛ - تستدعي
publish(title)كل استدعاء مع العنوان، بترتيب الاشتراك، وتُرجع عدد الذين تم إخطارهم.
يقرأ الكود المزوّد الأوامر sub ada وunsub ada وpost Big news، ويمنح كل قارئ استدعاءً يطبع ada got: Big news. ولكل منشور، يطبع العنوان، وأسطر الاستدعاءات، وعدد الذين تم إخطارهم.
يجب أن تضع كودك في Newsletter.R. يحتوي main.R على كود الإدخال/الإخراج المزوّد ولا يمكن تعديله.
جرّب بنفسك
source("Newsletter.R")
# كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
input <- suppressWarnings(readLines(file("stdin")))
news <- make_newsletter()
reader <- function(name) {
force(name)
function(title) cat(" ", name, " got: ", title, "\n", sep = "")
}
for (line in input) {
p <- strsplit(line, " ")[[1]]
if (p[1] == "sub") news$subscribe(p[2], reader(p[2]))
if (p[1] == "unsub") news$unsubscribe(p[2])
if (p[1] == "post") {
title <- paste(p[-1], collapse = " ")
cat("post: ", title, "\n", sep = "")
n <- news$publish(title)
cat(" notified ", n, "\n", sep = "")
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات S3
التعامل مع الملفاتالقوائم باستخدام فئةدوال المُنشئأساليب الطباعة والتنسيقأدوات التحقق والمساعدةمراجعة - درجات الحرارة2S3 الدوال العامة والطرق
الدوال العامة وUseMethodالطرق الافتراضيةطرق الدوال العامة الأساسيةأساسيات NextMethodمراجعة - مساحات الأشكالتدرّب بنفسك: مترجم R عبر الإنترنت