Patrón Factory
Parte de la sección Programación orientada a objetos del Journey de R de Coddy. Lección 43 de 57.
Una fábrica es una función que crea objetos, de modo que quien la llama no necesita conocer la clase ni su constructor. Recibe una descripción, como un nombre de tipo, y devuelve el objeto correcto:
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")Salida:
circle 12.57
square 4 Para un tipo que no reconoce, switch() devuelve NULL. Una fábrica debería detenerse con un mensaje que indique el tipo en su lugar, para que el error se muestre donde se cometió:
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)Salida:
unknown shape: hexagonUn registro mantiene los constructores en una lista con nombre, por lo que la fábrica en sí nunca cambia. Aquí, el registro es un entorno, y register_shape() añade un nuevo tipo mientras se ejecuta el programa:
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)))Salida:
[1] "rect"
[1] "circle" "rect" El código que utiliza los objetos depende únicamente de los genéricos, como area(). Una nueva clase que se registra y tiene un método area() funciona con ese código sin necesidad de modificarlo:
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))))Salida:
[1] 10Desafío
FácilUn registro puede exportarse en varios formatos. Exporters.R contiene el export(exporter, record) genérico y el método para csv_exporter, que devuelve los valores unidos por comas. Completa allí los otros dos métodos:
json_exporter:{"name": "Ada", "age": "36"};text_exporter:name=Ada; age=36.
Después escribe la fábrica make_exporter(format) en Factory.R: csv, json o text devuelven una lista vacía con la clase csv_exporter, json_exporter o text_exporter, y cualquier otro formato se detiene con unknown format: xml (usa call. = FALSE). El código proporcionado lee los nombres de los campos y los valores, después los nombres de los formatos, e imprime cada exportación o error: y el mensaje.
Tu código va en Exporters.R y Factory.R. main.R contiene el código de entrada/salida proporcionado y no se puede editar.
Pruébalo tú mismo
source("Exporters.R")
source("Factory.R")
# Código de entrada/salida suministrado: déjalo tal 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 lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Fundamentos de S3
Trabajar con archivosListas con una claseFunciones constructorasMétodos de impresión y formatoValidadores y auxiliaresRepaso: temperaturas4Encapsulación en R
Cierres como objetosEntornos como objetosFunciones de accesoProtección del estadoRepaso: parquímetro2S3 Genéricos y métodos
Genéricos y UseMethodMétodos predeterminadosMétodos para genéricos baseFundamentos de NextMethodRepaso: áreas de figuras5Clases S4
Definición de clases S4Slots y accesoComprobaciones de validezMétodos showRepaso - Catálogo de productos8Clases de referencia
Definición de clases de referenciaMétodos y actualizaciones de camposSemántica de copiaHerencia y callSuperRepaso: cola de tareas11Proyecto: Gestión de biblioteca
Libros y miembrosPréstamo de librosPractica por tu cuenta: Compilador de R online