
Observer en Go
Observer es un patrón de diseño de comportamiento que permite a un objeto notificar a otros objetos sobre cambios en su estado.
El patrón Observer proporciona una forma de suscribirse y cancelar la subscripción a estos eventos para cualquier objeto que implementa una interfaz suscriptora.
Ejemplo conceptual
En el sitio web de comercio electrónico, los artículos se agotan de vez en cuando. Puede haber clientes interesados en un artículo concreto que se agotó. Hay tres soluciones a este problema:
- El cliente comprueba la disponibilidad del artículo cada cierto tiempo.
- El comercio electrónico bombardea a los clientes con los nuevos artículos disponibles en stock.
- El cliente se suscribe tan solo al artículo particular en el que está interesado y recibe notificaciones si éste se encuentra disponible. Además, varios clientes pueden suscribirse al mismo producto.
La opción 3 es la más viable, y en esto consiste el patrón Observer. Los principales componentes del patrón Observer son:
- Sujeto, la instancia que publica un evento cuando sucede algo.
- Observador, que se inscribe a los eventos del sujeto y recibe notificaciones cuando suceden.
subject.go: Sujeto
package main
type Subject interface {
register(observer Observer)
deregister(observer Observer)
notifyAll()
}
item.go: Sujeto concreto
package main
import "fmt"
type Item struct {
observerList []Observer
name string
inStock bool
}
func newItem(name string) *Item {
return &Item{
name: name,
}
}
func (i *Item) updateAvailability() {
fmt.Printf("Item %s is now in stock\n", i.name)
i.inStock = true
i.notifyAll()
}
func (i *Item) register(o Observer) {
i.observerList = append(i.observerList, o)
}
func (i *Item) deregister(o Observer) {
i.observerList = removeFromslice(i.observerList, o)
}
func (i *Item) notifyAll() {
for _, observer := range i.observerList {
observer.update(i.name)
}
}
func removeFromslice(observerList []Observer, observerToRemove Observer) []Observer {
observerListLength := len(observerList)
for i, observer := range observerList {
if observerToRemove.getID() == observer.getID() {
observerList[observerListLength-1], observerList[i] = observerList[i], observerList[observerListLength-1]
return observerList[:observerListLength-1]
}
}
return observerList
}
observer.go: Observador
package main
type Observer interface {
update(string)
getID() string
}
customer.go: Observador concreto
package main
import "fmt"
type Customer struct {
id string
}
func (c *Customer) update(itemName string) {
fmt.Printf("Sending email to customer %s for item %s\n", c.id, itemName)
}
func (c *Customer) getID() string {
return c.id
}
main.go: Código cliente
package main
func main() {
shirtItem := newItem("Nike Shirt")
observerFirst := &Customer{id: "abc@gmail.com"}
observerSecond := &Customer{id: "xyz@gmail.com"}
shirtItem.register(observerFirst)
shirtItem.register(observerSecond)
shirtItem.updateAvailability()
}
output.txt: Resultado de la ejecución
Item Nike Shirt is now in stock
Sending email to customer abc@gmail.com for item Nike Shirt
Sending email to customer xyz@gmail.com for item Nike Shirt
Basado en: Golang By Example