Wzorzec fabryki
Część sekcji Programowanie obiektowe ścieżki R w Coddy. Lekcja 43 z 57.
Fabryka to funkcja, która tworzy obiekty, dzięki czemu wywołujący nie musi znać klasy ani jej konstruktora. Przyjmuje opis, taki jak nazwa typu, i zwraca odpowiedni obiekt:
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")Wynik:
circle 12.57
square 4 Dla nieznanego typu switch() zwraca NULL. Zamiast tego fabryka powinna przerwać działanie i wyświetlić komunikat zawierający nazwę typu, aby błąd był widoczny w miejscu, w którym został popełniony:
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)Wynik:
unknown shape: hexagonRejestr przechowuje konstruktory na nazwanej liście, więc sama fabryka nigdy się nie zmienia. Tutaj rejestr jest środowiskiem, a register_shape() dodaje nowy rodzaj w trakcie działania programu:
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)))Wynik:
[1] "rect"
[1] "circle" "rect" Kod wykorzystujący obiekty zależy wyłącznie od funkcji generycznych, takich jak area(). Nowa klasa, która zostanie zarejestrowana i będzie miała metodę area(), będzie działać z tym kodem bez żadnych zmian:
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))))Wynik:
[1] 10Wyzwanie
ŁatwyRekord można wyeksportować w kilku formatach. Exporters.R zawiera ogólną funkcję export(exporter, record) oraz metodę dla csv_exporter, która zwraca wartości połączone przecinkami. Uzupełnij tam dwie pozostałe metody:
json_exporter:{"name": "Ada", "age": "36"};text_exporter:name=Ada; age=36.
Następnie napisz fabrykę make_exporter(format) w Factory.R: csv, json lub text zwracają pustą listę z klasą csv_exporter, json_exporter lub text_exporter, a w przypadku każdego innego formatu działanie zostaje przerwane z komunikatem unknown format: xml (użyj call. = FALSE). Dostarczony kod odczytuje nazwy pól i wartości, a następnie nazwy formatów, i wyświetla wynik każdego eksportu albo error: oraz komunikat o błędzie.
Umieść swój kod w plikach Exporters.R i Factory.R. Plik main.R zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
source("Exporters.R")
source("Factory.R")
# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
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 = "")
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy S3
Praca z plikamiListy z klasąFunkcje konstruktoraMetody print i formatWalidatory i funkcje pomocniczePowtórzenie — temperatury4Enkapsulacja w R
Domknięcia jako obiektyŚrodowiska jako obiektyFunkcje dostępoweOchrona stanuPodsumowanie – parkometr2Generics i metody S3
Generics i UseMethodMetody domyślneMetody dla podstawowych genericsPodstawy NextMethodPowtórzenie — pola figur5Klasy S4
Definiowanie klas S4Sloty i dostępSprawdzanie poprawnościMetody wyświetlaniaPodsumowanie – katalog produktów8Klasy referencyjne
Definiowanie klas referencyjnychMetody i aktualizacja pólSemantyka kopiowaniaDziedziczenie i callSuperPodsumowanie – kolejka zadań11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książek3Dziedziczenie S3
Wektory klasKolejność wyszukiwania metodRozszerzanie za pomocą NextMethodSprawdzanie za pomocą inherits()Powtórzenie — lista płac pracownikówPoćwicz samodzielnie: Kompilator R online