빌더 패턴
Coddy R 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 47번째.
빌더는 복잡한 객체를 단계별로 조립하고 마지막에 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 = "")
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 R 컴파일러