策略模式
本课属于 Coddy R 学习旅程的“面向对象编程”单元。第 45 课,共 57 课。
策略模式让算法可以替换:需要该算法的代码会将它作为参数接收。在 R 中,函数是一种值,因此策略就是传入的函数,由调用者选择使用哪一个:
no_discount <- function(total) total
half_price <- function(total) total / 2
checkout <- function(total, discount = no_discount) {
paste("pay", discount(total))
}
print(checkout(80))
print(checkout(80, half_price))输出:
[1] "pay 80"
[1] "pay 40"为策略命名后,程序就能在运行时按名称选择策略,例如根据用户输入进行选择:
pricing <- list(
regular = function(p) p,
member = function(p) p * 0.9,
staff = function(p) p * 0.7
)
for (who in c("regular", "member", "staff")) cat(who, pricing[[who]](50), "\n")输出:
regular 50
member 45
staff 35 带设置的策略由工厂创建。percent_off(10)会返回一个新的策略函数,并记住它的百分比:
percent_off <- function(pct) function(total) total * (1 - pct / 100)
fixed_off <- function(amount) function(total) max(0, total - amount)
rules <- list(percent_off(10), percent_off(25), fixed_off(30))
print(vapply(rules, function(r) r(100), numeric(1)))输出:
[1] 90 75 70对象可以将其策略保存在一个字段中,并在之后进行替换。对象的其余部分保持不变:
new_sorter <- function(key) structure(list(key = key), class = "sorter")
sort_with <- function(s, words) words[order(s$key(words))]
by_length <- function(w) nchar(w)
by_last_letter <- function(w) substring(w, nchar(w))
s <- new_sorter(by_length)
words <- c("pear", "fig", "banana")
print(sort_with(s, words))
s$key <- by_last_letter
print(sort_with(s, words))输出:
[1] "fig" "pear" "banana"
[1] "banana" "fig" "pear" 挑战
简单配送方式由策略决定:策略是一个函数,接收购物车的总重量和小计,并返回运费。在 Strategies.R 中编写:
standard(kg, subtotal):4.99 加上每千克 1;express(kg, subtotal):标准运费的两倍;free_over(limit):返回一个策略,当小计大于或等于limit时运费为 0,否则运费为标准运费。
在 Cart.R 中,已提供 new_cart(strategy) 和 add_item();请完成 subtotal(cart) 和 shipping_cost(cart),后者会调用购物车的策略。已提供的代码会读取策略(standard、express 或 free_over 50),接着读取商品 book 12 0.5(名称、价格、千克数),并输出总额。
你的代码应写在 Strategies.R 和 Cart.R 中。main.R 中包含已提供的输入输出代码,不能编辑。
亲手试试
source("Strategies.R")
source("Cart.R")
# 提供的输入和输出代码:保持原样。
input <- suppressWarnings(readLines(file("stdin")))
p <- strsplit(input[1], " ")[[1]]
rule <- if (p[1] == "free_over") free_over(as.numeric(p[2])) else get(p[1])
cart <- new_cart(rule)
for (line in input[-1]) {
q <- strsplit(line, " ")[[1]]
cart <- add_item(cart, q[1], as.numeric(q[2]), as.numeric(q[3]))
}
cat(sprintf("items: %.2f, shipping: %.2f, total: %.2f", subtotal(cart), shipping_cost(cart), subtotal(cart) + shipping_cost(cart)), "\n", sep = "")
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: R 在线编译器