Шаблон «Декоратор»
Часть раздела Объектно-ориентированное программирование путешествия по R на Coddy. Урок 46 из 57.
Декоратор оборачивает что-либо и добавляет поведение, сохраняя тот же интерфейс. Для функции декоратор принимает функцию и возвращает новую функцию, которая выполняет дополнительную работу до и после вызова:
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))Вывод:
calling double with 21
[1] 42Декоратор может запоминать результаты. with_cache() сохраняет каждый ответ в окружении и вызывает обёрнутую функцию только для новых аргументов:
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)Вывод:
results computed: 2Объекты тоже можно декорировать. Каждый декоратор — это объект, который хранит обёрнутый им объект в поле и отвечает на те же обобщённые функции, добавляя свою часть и запрашивая остальное у внутреннего объекта:
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))Вывод:
coffee + milk + milk = 3Декораторы объединяются в стек, и их порядок имеет значение: самый внешний выполняется первым при входе и последним при выходе:
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"))Вывод:
hi ada! hi ada! / hi ada hi ada!Задание
ЛегкоНапитки украшаются дополнениями. Drink.R содержит обобщённые функции cost() и describe(), а также базовый напиток new_coffee(), который стоит 2 и описывает себя как coffee. В Extras.R напишите три декоратора, каждый из которых представляет собой объект, содержащий оборачиваемый им напиток в поле inner:
add_milk(d): классmilk, добавляет 0.50 и+ milk;add_sugar(d): классsugar, добавляет 0.20 и+ sugar;add_syrup(d, flavour): классsyrupс полемflavour, добавляет 0.80 и+ vanilla syrup.
Напишите их методы cost() и describe(), сначала обращаясь к внутреннему напитку. Предоставленный код считывает строки, такие как milk,syrup vanilla,sugar (пустая строка означает обычный кофе), и выводит coffee + milk + vanilla syrup + sugar: 3.50.
Ваш код должен находиться в Drink.R и Extras.R. В main.R содержится предоставленный код ввода/вывода, и его нельзя редактировать.
Попробуйте сами
source("Drink.R")
source("Extras.R")
# Предоставленный код ввода/вывода: оставьте его как есть
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 = "")
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы S3
Работа с файламиСписки с классомФункции-конструкторыМетоды print и formatВалидаторы и вспомогательные функцииПовторение — температуры4Инкапсуляция в R
Замыкания как объектыОкружения как объектыФункции доступаЗащита состоянияПовторение — парковочный счётчик7S4 Наследование
Расширение с помощью containscallNextMethodВиртуальные классыПроверка типов с помощью is()Повторение — смотритель зоопарка10Шаблоны проектирования, часть 2
Шаблон «Декоратор»Шаблон «Строитель»Шаблон «Состояние»Шаблон «Итератор»2Обобщённые функции и методы S3
Обобщённые функции и UseMethodМетоды по умолчаниюМетоды для базовых обобщённых функцийОсновы NextMethodПовторение — площади фигур5Классы S4
Определение классов S4Слоты и доступПроверка корректностиМетоды showПовторение — каталог товаров8Ссылочные классы
Определение ссылочных классовМетоды и обновление полейСемантика копированияНаследование и callSuperПовторение — очередь задач11Проект: Управление библиотекой
Книги и читателиВыдача книг3Наследование S3
Векторы классовПорядок поиска методовРасширение с помощью NextMethodПроверка с помощью inherits()Повторение — расчёт зарплаты сотрудниковПотренируйтесь самостоятельно: Онлайн-компилятор R