옵저버 패턴
Coddy R 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 44번째.
옵서버 패턴을 사용하면 객체인 주체가 누가 듣고 있는지 알지 못해도 어떤 일이 발생했을 때 다른 코드에 알릴 수 있습니다. 주체는 콜백 함수의 목록인 옵서버들을 유지하고, 각 이벤트마다 모든 옵서버를 호출합니다:
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 = "")
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 R 컴파일러