Patrón decorador
Parte de la sección Programación orientada a objetos del Journey de Swift de Coddy. Lección 47 de 57.
El patrón decorator añade comportamiento a un objeto envolviéndolo en otro objeto con el mismo protocolo. El envoltorio reenvía la llamada al objeto envuelto y añade su propia parte antes o después:
protocol Coffee {
func cost() -> Int
func label() -> String
}
struct Espresso: Coffee {
func cost() -> Int { 200 }
func label() -> String { "espresso" }
}
struct WithMilk: Coffee {
let base: any Coffee
func cost() -> Int { base.cost() + 50 }
func label() -> String { base.label() + " + milk" }
}
let drink = WithMilk(base: Espresso())
print(drink.label())
print(drink.cost())Salida:
espresso + milk
250Como un decorador es en sí mismo un Coffee, los decoradores pueden envolverse entre sí en cualquier combinación y orden, sin una subclase para cada combinación:
protocol Coffee { func cost() -> Int; func label() -> String }
struct Espresso: Coffee { func cost() -> Int { 200 }; func label() -> String { "espresso" } }
struct WithMilk: Coffee {
let base: any Coffee
func cost() -> Int { base.cost() + 50 }
func label() -> String { base.label() + " + milk" }
}
struct WithSyrup: Coffee {
let base: any Coffee
func cost() -> Int { base.cost() + 30 }
func label() -> String { base.label() + " + syrup" }
}
let fancy = WithSyrup(base: WithMilk(base: WithMilk(base: Espresso())))
print(fancy.label())
print(fancy.cost())Salida:
espresso + milk + milk + syrup
330Los decoradores permiten añadir funcionalidades transversales, como registro, almacenamiento en caché o validación, alrededor de un componente existente, que permanece sin cambios:
protocol DataSource { func fetch(_ id: Int) -> String }
struct Database: DataSource { func fetch(_ id: Int) -> String { "row \(id)" } }
struct Logged: DataSource {
let inner: any DataSource
func fetch(_ id: Int) -> String {
print("fetching \(id)")
return inner.fetch(id)
}
}
let source: any DataSource = Logged(inner: Database())
print(source.fetch(7))Salida:
fetching 7
row 7El código que utiliza el objeto solo ve el protocolo, por lo que no puede distinguir un objeto decorado de uno simple. Eso es lo que hace que el patrón se pueda añadir más adelante de forma segura.
func printReceipt(_ c: any Coffee) { // funciona para Espresso y para cualquier versión envuelta
print("\(c.label()): \(c.cost())")
}Desafío
FácilLas pizzas se construyen con decoradores. El protocolo Pizza tiene price() y toppings() (un array de nombres). Base cuesta 800 y no tiene ingredientes. Implementa los decoradores Cheese (+150), Olives (+100) y Double (duplica el precio de todo lo que contiene y repite dos veces sus ingredientes). Completa build(_:), que comienza con Base y la envuelve con un decorador por cada nombre, en orden.
El código proporcionado lee líneas de nombres de ingredientes separados por comas (o una línea vacía), construye cada pizza e imprime 1050: cheese, olives o 800: plain.
Tu código va en Pizza.swift, Base.swift, Cheese.swift, Olives.swift, Double.swift y Functions.swift. main.swift contiene el código proporcionado de entrada/salida y no se puede editar.
Pruébalo tú mismo
// Código de entrada/salida suministrado: mantenlo como está
var input: [String] = []
while let line = readLine() { input.append(line) }
for line in input {
let names = line.isEmpty ? [] : line.split(separator: ",").map(String.init)
let p = build(names)
let tops = p.toppings()
print("\(p.price()): " + (tops.isEmpty ? "plain" : tops.joined(separator: ", ")))
}
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
1Clases y objetos
Trabajar con archivosDefinir clasesMétodos y selfInicializadoresLas clases son referenciasRepaso: tarjeta de biblioteca4Encapsulación
Control de accesoSolo lectura desde fueraProtección del estadoInicializadores que pueden fallarRepaso - Cuenta bancaria7Polimorfismo y genéricos
PolimorfismoConversión de tiposFunciones genéricasTipos genéricosRepaso - Calculadora de formas2Structs y tipos por valor
StructsSemántica de valoresMétodos mutablesStruct o claseRepaso - elemento del inventario5Herencia
SubclasesSobrescritura de métodosInicializadores y superfinal y jerarquíasRepaso: jerarquía de empleados8Protocolos estándar
CustomStringConvertibleEquatableComparableHashableRepaso - Ordenamiento personalizado11Proyecto: Gestión de bibliotecas
Libros y miembrosPréstamo de librosPractica por tu cuenta: Playground de Swift