Menu
Coddy logo textTech

Padrão Factory

Parte da seção Programação Orientada a Objetos do Journey de R da Coddy. Lição 43 de 57.

Uma fábrica é uma função que cria objetos para que o chamador não precise conhecer a classe nem seu construtor. Ela recebe uma descrição, como um nome de tipo, e retorna o objeto correto:

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")

Saída:

circle 12.57 
square 4 

Para um tipo que não conhece, switch() retorna NULL. Uma fábrica deve parar com uma mensagem que nomeie o tipo, para que o erro apareça no local em que foi cometido:

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)

Saída:

unknown shape: hexagon

Um registro mantém os construtores em uma lista nomeada, portanto a própria fábrica nunca muda. Aqui, o registro é um ambiente, e register_shape() adiciona um novo tipo enquanto o programa é executado:

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)))

Saída:

[1] "rect"
[1] "circle" "rect"  

O código que usa os objetos depende apenas dos genéricos, como area(). Uma nova classe que seja registrada e tenha um método area() funciona com esse código sem nenhuma alteração:

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))))

Saída:

[1] 10
challenge icon

Desafio

Fácil

Um registro pode ser exportado em vários formatos. Exporters.R contém a export(exporter, record) genérica e o método para csv_exporter, que retorna os valores unidos por vírgulas. Complete os outros dois métodos nesse arquivo:

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

Depois, escreva a fábrica make_exporter(format) em Factory.R: csv, json ou text retornam uma lista vazia com a classe csv_exporter, json_exporter ou text_exporter, e qualquer outro formato interrompe a execução com unknown format: xml (use call. = FALSE). O código fornecido lê os nomes dos campos e os valores, depois os nomes dos formatos, e imprime cada exportação ou error: e a mensagem.

Seu código deve estar em Exporters.R e Factory.R. main.R contém o código de entrada/saída fornecido e não pode ser editado.

Experimente você mesmo

source("Exporters.R")
source("Factory.R")

# Código de entrada/saída fornecido: mantenha-o como está
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 iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de R online