Wzorzec Dekorator
Część sekcji Programowanie obiektowe ścieżki R w Coddy. Lekcja 46 z 57.
Dekorator opakowuje coś i dodaje zachowanie, zachowując ten sam interfejs. W przypadku funkcji dekorator przyjmuje funkcję i zwraca nową funkcję, która wykonuje dodatkowe działania wokół wywołania:
with_logging <- function(f, name) {
function(x) {
cat("calling", name, "with", x, "\n")
f(x)
}
}
double <- function(x) x * 2
logged <- with_logging(double, "double")
print(logged(21))Wynik:
calling double with 21
[1] 42Dekorator może zapamiętywać wyniki. with_cache() przechowuje każdą odpowiedź w środowisku i wywołuje opakowaną funkcję tylko dla nowych argumentów:
with_cache <- function(f) {
cache <- new.env()
function(x) {
key <- as.character(x)
if (!exists(key, envir = cache, inherits = FALSE)) assign(key, f(x), envir = cache)
get(key, envir = cache)
}
}
work <- 0
slow_square <- function(x) {
work <<- work + 1
x^2
}
fast <- with_cache(slow_square)
for (x in c(3, 4, 3, 3, 4)) fast(x)
cat("results computed:", work)Wynik:
results computed: 2Obiekty również można dekorować. Każdy dekorator jest obiektem, który przechowuje opakowywany obiekt w polu i odpowiada na te same metody generyczne, dodając własny element i pytając obiekt wewnętrzny o resztę:
cost <- function(d) UseMethod("cost")
describe <- function(d) UseMethod("describe")
cost.coffee <- function(d) 2
describe.coffee <- function(d) "coffee"
with_milk <- function(inner) structure(list(inner = inner), class = "milk")
cost.milk <- function(d) cost(d$inner) + 0.5
describe.milk <- function(d) paste(describe(d$inner), "+ milk")
cup <- with_milk(with_milk(structure(list(), class = "coffee")))
cat(describe(cup), "=", cost(cup))Wynik:
coffee + milk + milk = 3Dekoratory można nakładać jeden na drugi, a ich kolejność ma znaczenie: najbardziej zewnętrzny uruchamia się jako pierwszy przy wejściu i jako ostatni przy wyjściu:
exclaim <- function(f) function(s) paste0(f(s), "!")
twice <- function(f) function(s) paste(f(s), f(s))
greet <- function(name) paste("hi", name)
a <- twice(exclaim(greet))
b <- exclaim(twice(greet))
cat(a("ada"), "/", b("ada"))Wynik:
hi ada! hi ada! / hi ada hi ada!Wyzwanie
ŁatwyNapoje są uzupełniane dodatkami. Drink.R zawiera funkcje generyczne cost() i describe() oraz napój bazowy new_coffee(), który kosztuje 2 i opisuje siebie jako coffee. W pliku Extras.R napisz trzy dekoratory, z których każdy jest obiektem przechowującym opakowany napój w polu inner:
add_milk(d): klasamilk, dodaje 0.50 i+ milk;add_sugar(d): klasasugar, dodaje 0.20 i+ sugar;add_syrup(d, flavour): klasasyrupz polemflavour, dodaje 0.80 i+ vanilla syrup.
Napisz ich metody cost() i describe(), najpierw wywołując je dla wewnętrznego napoju. Dostarczony kod odczytuje wiersze, takie jak milk,syrup vanilla,sugar (pusty wiersz oznacza zwykłą kawę) i wypisuje coffee + milk + vanilla syrup + sugar: 3.50.
Umieść swój kod w plikach Drink.R i Extras.R. Plik main.R zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
source("Drink.R")
source("Extras.R")
# Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
d <- new_coffee()
if (nchar(line) > 0) for (extra in strsplit(line, ",")[[1]]) {
p <- strsplit(extra, " ")[[1]]
d <- switch(p[1], milk = add_milk(d), sugar = add_sugar(d), syrup = add_syrup(d, p[2]))
}
cat(sprintf("%s: %.2f", describe(d), cost(d)), "\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