Patrón de delegación
Parte de la sección Programación orientada a objetos del Journey de Swift de Coddy. Lección 46 de 57.
La delegación permite que un objeto deje algunas decisiones en manos de otro objeto, su delegado, mediante un protocolo. El objeto realiza su tarea y consulta al delegado en los puntos que varían. Los frameworks de Apple utilizan este patrón en todas partes:
protocol DownloadDelegate: AnyObject {
func progress(_ percent: Int)
func finished(_ file: String)
}
final class Downloader {
weak var delegate: DownloadDelegate?
func download(_ file: String) {
for p in [50, 100] { delegate?.progress(p) }
delegate?.finished(file)
}
}
final class Screen: DownloadDelegate {
func progress(_ percent: Int) { print("\(percent)%") }
func finished(_ file: String) { print("done: \(file)") }
}
let screen = Screen()
let d = Downloader()
d.delegate = screen
d.download("photo.png")Salida:
50%
100%
done: photo.pngLa propiedad delegate es opcional y las llamadas usan delegate?., por lo que el objeto funciona incluso cuando nadie está escuchando. Se declara weak para que el objeto no mantenga vivo a su delegate por sí mismo, lo que evita ciclos de retención entre objetos que apuntan el uno al otro:
weak var delegate: DownloadDelegate? // requiere que el protocolo sea solo de clase: AnyObject
delegate?.finished(file) // no hace nada cuando no hay delegateUn delegado también puede responder preguntas, lo que le permite personalizar el comportamiento del objeto sin crear una subclase:
protocol ListDelegate: AnyObject {
func shouldShow(_ item: String) -> Bool
}
final class ListView {
weak var delegate: ListDelegate?
func render(_ items: [String]) -> [String] {
items.filter { delegate?.shouldShow($0) ?? true }
}
}
final class HideDrafts: ListDelegate {
func shouldShow(_ item: String) -> Bool { !item.hasPrefix("draft") }
}
let view = ListView()
let rules = HideDrafts()
print(view.render(["a", "draft b"]))
view.delegate = rules
print(view.render(["a", "draft b"]))Salida:
["a", "draft b"]
["a"]La delegación se diferencia del patrón de observador: un objeto tiene un delegado al que pregunta y mantiene informado, mientras que un sujeto puede tener muchos observadores a los que solo informa.
// delegación: un socio, puede responder preguntas
weak var delegate: TableDelegate?
// observador: cualquier número de oyentes, solo notificados
private var observers: [(Event) -> Void] = []Desafío
FácilUn Checkout pide decisiones a su delegado. El protocolo CheckoutDelegate (solo para clases) tiene discount(for total: Int) -> Int y didComplete(total: Int). Checkout tiene un weak var delegate, y pay(_ prices: [Int]) -> Int suma los precios, resta el descuento del delegado (0 sin delegado), informa al delegado del total final y lo devuelve. Implementa LoyaltyShop, un delegado que ofrece un 10 % de descuento en totales de 100 o más y registra cada total completado en receipts.
El código proporcionado lee líneas de precios separados por comas. Paga la primera línea sin delegado y las demás con un LoyaltyShop, imprime cada total y finalmente receipts: 120 90.
Tu código debe estar en CheckoutDelegate.swift, Checkout.swift y LoyaltyShop.swift. main.swift contiene el código de entrada/salida proporcionado y no se puede editar.
Pruébalo tú mismo
// Código de entrada/salida suministrado: déjalo como está
var input: [String] = []
while let line = readLine() { input.append(line) }
let checkout = Checkout()
let shop = LoyaltyShop()
for (i, line) in input.enumerated() {
if i == 1 { checkout.delegate = shop }
print(checkout.pay(line.split(separator: ",").map { Int($0)! }))
}
print("receipts: " + shop.receipts.map { String($0) }.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