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: hexagonUm 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] 10Desafio
FácilUm 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 = "")
}
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
1Fundamentos do S3
Trabalhando com ArquivosListas com uma ClasseFunções ConstrutorasMétodos de Impressão e FormataçãoValidadores e AuxiliaresRevisão - Temperaturas4Encapsulamento em R
Closures como ObjetosAmbientes como ObjetosFunções de AcessoProtegendo o EstadoRecapitulação - Parquímetro2Genéricos e Métodos S3
Genéricos e UseMethodMétodos padrãoMétodos para genéricos baseFundamentos de NextMethodRevisão - Áreas de formas5Classes S4
Definindo classes S4Slots e acessoVerificações de validadeMétodos showRevisão - Catálogo de produtos8Classes de Referência
Definindo Classes de ReferênciaMétodos e Atualizações de CamposSemântica de CópiaHerança e callSuperRecapitulação — Fila de Tarefas11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Compilador de R online