Padrão Observer
Parte da seção Programação Orientada a Objetos do Journey de Swift da Coddy. Lição 44 de 57.
No padrão observer, um objeto (o subject) mantém uma lista de observadores e notifica todos eles quando algo acontece. O subject não precisa saber o que os observadores fazem:
final class Button {
private var handlers: [(String) -> Void] = []
func onTap(_ handler: @escaping (String) -> Void) {
handlers.append(handler)
}
func tap() {
for h in handlers { h("tapped") }
}
}
let b = Button()
b.onTap { print("logger saw \($0)") }
b.onTap { print("counter saw \($0)") }
b.tap()Saída:
logger saw tapped
counter saw tappedOs observadores também podem ser objetos que estão em conformidade com um protocolo. O sujeito chama o método do protocolo em cada observador registrado:
protocol StockObserver: AnyObject {
func priceChanged(_ symbol: String, to price: Int)
}
final class Stock {
let symbol: String
private var observers: [StockObserver] = []
var price: Int { didSet { observers.forEach { $0.priceChanged(symbol, to: price) } } }
init(symbol: String, price: Int) { self.symbol = symbol; self.price = price }
func add(_ o: StockObserver) { observers.append(o) }
}
final class Alert: StockObserver {
func priceChanged(_ s: String, to p: Int) { if p > 100 { print("\(s) above 100: \(p)") } }
}
let apple = Stock(symbol: "AAPL", price: 90)
apple.add(Alert())
apple.price = 95
apple.price = 120Saída:
AAPL above 100: 120Os observadores geralmente precisam de uma maneira de parar de escutar. Retornar um id (ou token) do registro permite que o chamador remova seu observador posteriormente:
final class EventBus {
private var listeners: [Int: (String) -> Void] = [:]
private var nextId = 0
func subscribe(_ f: @escaping (String) -> Void) -> Int {
nextId += 1
listeners[nextId] = f
return nextId
}
func unsubscribe(_ id: Int) { listeners[id] = nil }
func publish(_ event: String) {
for id in listeners.keys.sorted() { listeners[id]!(event) }
}
}
let bus = EventBus()
let a = bus.subscribe { print("A got \($0)") }
_ = bus.subscribe { print("B got \($0)") }
bus.publish("login")
bus.unsubscribe(a)
bus.publish("logout")Saída:
A got login
B got login
B got logout@escaping indica um closure que é armazenado e chamado posteriormente, depois que a função que o recebeu retorna. O padrão Observer mantém o sujeito e os observadores pouco acoplados: novos observadores podem ser adicionados sem alterar o sujeito.
func onTap(_ handler: @escaping (String) -> Void) {
handlers.append(handler) // armazenado: deve ser @escaping
}Desafio
FácilComplete a classe Newsletter, um assunto que envia edições aos assinantes. subscribe(name:handler:) armazena um closure sob um nome (assinar novamente com o mesmo nome o substitui), unsubscribe(name:) o remove, e publish(_ title:) chama cada handler em ordem alfabética de nome e retorna quantos foram notificados.
O código fornecido lê os comandos sub <name>, unsub <name> ou pub <title>. O handler de cada assinante imprime <name> reads <title>; após cada pub, o código imprime sent to 2.
Seu código deve estar em Newsletter.swift. main.swift contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
// Código de entrada/saída fornecido: mantenha-o como está
var input: [String] = []
while let line = readLine() { input.append(line) }
let news = Newsletter()
for cmd in input {
let p = cmd.split(separator: " ", maxSplits: 1).map(String.init)
switch p[0] {
case "sub":
let name = p[1]
news.subscribe(name: name) { print("\(name) reads \($0)") }
case "unsub": news.unsubscribe(name: p[1])
default: print("sent to \(news.publish(p[1]))")
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Classes e Objetos
Trabalhando com ArquivosDefinindo ClassesMétodos e selfInicializadoresClasses são ReferênciasRevisão - Cartão da Biblioteca4Encapsulamento
Controle de AcessoSomente Leitura ExternaProtegendo o EstadoInicializadores que Podem FalharRevisão - Conta Bancária7Polimorfismo e Genéricos
PolimorfismoConversão de TiposFunções GenéricasTipos GenéricosRevisão - Calculadora de Formas2Structs e Tipos de Valor
StructsSemântica de ValorMétodos MutáveisStruct ou ClasseRecapitulação - Item de Inventário5Herança
SubclassesSobrescrita de métodosInicializadores e superfinal e hierarquiasRecapitulação - Hierarquia de funcionários8Protocolos Padrão
CustomStringConvertibleEquatableComparableHashableRecapitulação - Ordenação Personalizada11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de LivrosPratique por conta própria: Playground de Swift