Menu
Coddy logo textTech

팩토리 패턴

Coddy R 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 57개 중 43번째.

팩토리는 호출자가 클래스나 해당 생성자를 알 필요가 없도록 객체를 생성하는 함수입니다. 타입 이름과 같은 설명을 받아 올바른 객체를 반환합니다:

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

챌린지

쉬움

레코드는 여러 형식으로 내보낼 수 있습니다. Exporters.R에는 일반적인 export(exporter, record)와 쉼표로 결합한 값을 반환하는 csv_exporter 메서드가 들어 있습니다. 그곳에서 나머지 두 메서드를 완성하세요:

  • json_exporter: {"name": "Ada", "age": "36"};
  • text_exporter: name=Ada; age=36.

그런 다음 Factory.R에서 팩토리 make_exporter(format)를 작성하세요. 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 = "")
}
quiz icon실력 점검

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

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

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