Menu
Coddy logo textTech

빌더 패턴

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 basil

build()는 객체가 완전한지 확인하는 곳입니다. 누락된 부분이 있으면 불완전한 객체를 생성하는 대신 메시지와 함께 중단됩니다:

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()
challenge icon

챌린지

쉬움

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 = "")
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 R 컴파일러