Esta publicación tiene como objetivo explicar cómo implementar un detector de eventos utilizando canales Go, lo que se podría llamar un observador en términos de patrones de diseño.
El problema
Tengo un perro que a veces ladra, y cuando esto sucede, suelo decirle que no ladre. Y como todos los perros, a veces hace caca, así que necesito recogerlo.
Modelaré a mi perro como una estructura que tiene una lista de oyentes de eventos, que es una lista de personas que lo cuidan.
El perro emitirá eventos a esos oyentes cuando ocurra algo.
Así es como modelaríamos a nuestro perro en Go: tenemos una estructura con un nombre y una lista de cuidadores, y 3 eventos: podemos agregar un nuevo cuidador de perros, podemos eliminar uno y nuestro perro puede emitir eventos.
// Dog has a name and a list of people caring for him and watching
// what he does
type Dog struct {
name string
sitters map[string][]chan string
}
// AddSitter adds an event listener to the Dog struct instance
func (b *Dog) AddSitter(e string, ch chan string) {
if b.sitters == nil {
b.sitters = make(map[string][]chan string)
}
if _, ok := b.sitters[e]; ok {
b.sitters[e] = append(b.sitters[e], ch)
} else {
b.sitters[e] = []chan string{ch}
}
}
// RemoveSitter removes an event listener from the Dog struct instance
func (b *Dog) RemoveSitter(e string, ch chan string) {
if _, ok := b.sitters[e]; ok {
for i := range b.sitters[e] {
if b.sitters[e][i] == ch {
b.sitters[e] = append(b.sitters[e][:i], b.sitters[e][i+1:]…)
break
}
}
}
}
// Emit emits an event on the Dog struct instance
func (b *Dog) Emit(e string, response string) {
if , ok := b.sitters[e]; ok {
for , handler := range b.sitters[e] {
go func(handler chan string) {
handler <- response
}(handler)
}
}
}
Corriendo a nuestro perro
Llevemos al perro al mundo y veamos cómo funciona. Primero soy el único que cuida al perro, así que me agregaré como cuidador de perros conbalto.AddSitter()
para todos los eventos que puedo manejar:bark
,poop
,hungry
.
Después de un tiempo, me cansé de recoger la caca y contraté a un cuidador de perros para que hiciera el trabajo por mí, pero todavía me encargaré de decirle al perro que no ladre y lo alimente.
package main
import (
“fmt”
“time”
)
func main() {
balto := Dog{“Balto”, nil}
<span style="color:#a6e22e">flavio</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">chan</span> <span style="color:#66d9ef">string</span>)
<span style="color:#a6e22e">balto</span>.<span style="color:#a6e22e">AddSitter</span>(<span style="color:#e6db74">"bark"</span>, <span style="color:#a6e22e">flavio</span>)
<span style="color:#a6e22e">balto</span>.<span style="color:#a6e22e">AddSitter</span>(<span style="color:#e6db74">"poop"</span>, <span style="color:#a6e22e">flavio</span>)
<span style="color:#a6e22e">balto</span>.<span style="color:#a6e22e">AddSitter</span>(<span style="color:#e6db74">"hungry"</span>, <span style="color:#a6e22e">flavio</span>)
<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
<span style="color:#66d9ef">for</span> {
<span style="color:#a6e22e">msg</span> <span style="color:#f92672">:=</span> <span style="color:#f92672"><-</span><span style="color:#a6e22e">flavio</span>
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"Flavio: "</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">msg</span>)
}
}()
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"The dog barked!"</span>)
<span style="color:#a6e22e">balto</span>.<span style="color:#a6e22e">Emit</span>(<span style="color:#e6db74">"bark"</span>, <span style="color:#e6db74">"Told not to bark!"</span>)
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"The dog pooped!"</span>)
<span style="color:#a6e22e">balto</span>.<span style="color:#a6e22e">Emit</span>(<span style="color:#e6db74">"poop"</span>, <span style="color:#e6db74">"Picked up poop!"</span>)
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"The dog is hungry!"</span>)
<span style="color:#a6e22e">balto</span>.<span style="color:#a6e22e">Emit</span>(<span style="color:#e6db74">"hungry"</span>, <span style="color:#e6db74">"Feed the dog!"</span>)
<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">3</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"\n.\n.\n.\n"</span>)
<span style="color:#a6e22e">balto</span>.<span style="color:#a6e22e">RemoveSitter</span>(<span style="color:#e6db74">"poop"</span>, <span style="color:#a6e22e">flavio</span>)
<span style="color:#75715e">// Hired a dog sitter to pick up poop
dogsitter := make(chan string)
balto.AddSitter(“poop”, dogsitter)
fmt.Println(“Flavio hired a dogsitter to pick up poop, won’t pick it up again”)
<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
<span style="color:#66d9ef">for</span> {
<span style="color:#a6e22e">msg</span> <span style="color:#f92672">:=</span> <span style="color:#f92672"><-</span><span style="color:#a6e22e">dogsitter</span>
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"Dogsitter: "</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">msg</span>)
}
}()
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"Dog barked!"</span>)
<span style="color:#a6e22e">balto</span>.<span style="color:#a6e22e">Emit</span>(<span style="color:#e6db74">"bark"</span>, <span style="color:#e6db74">"Told not to bark!"</span>)
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"Dog has pooped!"</span>)
<span style="color:#a6e22e">balto</span>.<span style="color:#a6e22e">Emit</span>(<span style="color:#e6db74">"poop"</span>, <span style="color:#e6db74">"Picked up poop!"</span>)
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"Dog has pooped again!"</span>)
<span style="color:#a6e22e">balto</span>.<span style="color:#a6e22e">Emit</span>(<span style="color:#e6db74">"poop"</span>, <span style="color:#e6db74">"Picked up poop, again!"</span>)
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">"The dog is hungry!"</span>)
<span style="color:#a6e22e">balto</span>.<span style="color:#a6e22e">Emit</span>(<span style="color:#e6db74">"hungry"</span>, <span style="color:#e6db74">"Feed the dog!"</span>)
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Scanln</span>()
}
Estoy usandofmt.Scanln()
al final para evitar que el programa se cierre cuandomain()
ha terminado, mientras espera que se completen los eventos de manejo de goroutines.
Si lo ejecuto, parece que nuestro nuevo empleado también está haciendo el trabajo que esperamos de él, y podemos relajarnos:
$ go run eventlistener.go
The dog barked!
The dog pooped!
The dog is hungry!
Flavio: Picked up poop!
Flavio: Told not to bark!
Flavio: Feed the dog!
.
.
.
Flavio hired a dogsitter to pick up poop, won’t pick it up again
Dog barked!
Dog has pooped!
Dog has pooped again!
The dog is hungry!
Flavio: Told not to bark!
Dogsitter: Picked up poop!
Dogsitter: Picked up poop, again!
Flavio: Feed the dog!
Más tutoriales de go:
- Uso de NGINX Reverse Proxy para brindar servicios de Go
- Hacer una copia de una estructura en Go
- Los conceptos básicos de un servidor web Go
- Ordenar un tipo de mapa en Go
- Ir consejos en pocas palabras
- Explicación de las etiquetas Go
- Ir al formato de fecha y hora
- Procesamiento JSON con Go
- Ir a funciones variadas
- Hoja de referencia de Go Strings
- Explicación de la interfaz Go Empty
- Depuración de Go con VS Code y Delve
- Parámetros de devoluciones de Go Named
- Generación de cadenas y números aleatorios en Go
- Estructura del sistema de archivos de un proyecto de Go
- Algoritmo de búsqueda binaria implementado en Go
- Uso de indicadores de línea de comando en Go
- GOPATH explicado
- Cree una aplicación de línea de comandos con Go: lolcat
- Construyendo un comando CLI con Go: cowsay
- Uso de Shell Pipes con Go
- Tutorial de Go CLI: clon de la fortuna
- Enumere los archivos en una carpeta con Go
- Use Ir para obtener una lista de repositorios de GitHub
- Ve, agrega un trozo de cadenas a un archivo
- Ve, convierte una cadena en un segmento de bytes
- Visualice sus contribuciones locales de Git con Go
- Introducción a la creación de perfiles de memoria y CPU de Go
- Resolver el error "no admite la indexación" en un programa Go
- Medir el tiempo de ejecución en un programa Go
- Creación de un rastreador web con Go para detectar títulos duplicados
- Siga las mejores prácticas: ¿puntero o receptores de valor?
- Siga las mejores prácticas: ¿Debería utilizar un método o una función?
- Ir a estructuras de datos: Establecer
- Hoja de referencia de Go Maps
- Genere implementaciones para tipos genéricos en Go
- Ir a estructuras de datos: diccionario
- Ir a estructuras de datos: tabla hash
- Implementar oyentes de eventos en canales de paso
- Ir a estructuras de datos: apilar
- Ir a estructuras de datos: cola
- Ir a estructuras de datos: árbol de búsqueda binaria
- Ir a estructuras de datos: gráfico
- Ir a estructuras de datos: lista vinculada
- La guía completa de Go Data Structures
- Comparación de valores de Go
- ¿Go está orientado a objetos?
- Trabajar con una base de datos SQL en Go
- Usar variables de entorno en Go
- Ir al tutorial: API REST respaldada por PostgreSQL
- Habilitación de CORS en un servidor web Go
- Implementación de una aplicación Go en un contenedor Docker
- Por qué Go es un lenguaje poderoso para aprender como desarrollador PHP
- Ve, elimina el carácter de nueva línea io.Reader.ReadString
- Ir, cómo ver los cambios y reconstruir su programa
- Ve, cuenta los meses desde una fecha
- Acceder a los parámetros HTTP POST en Go