Implémenter des écouteurs d'événements dans Passer par les canaux

Cet article vise à expliquer comment implémenter un écouteur d'événement à l'aide des canaux Go, ce que l'on pourrait appeler un observateur en termes de design pattern.

Le problème

J'ai un chien qui aboie parfois, et quand cela arrive, je lui dis généralement de ne pas aboyer. Et comme tous les chiens, il fait parfois caca alors je dois le ramasser.

Je vais modéliser mon chien comme une structure qui a une liste d'auditeurs d'événements, c'est-à-dire une liste de personnes qui prennent soin de lui.

Le chien émettra des événements à ces auditeurs lorsque quelque chose se passera.

Voici comment nous modéliserons notre chien dans Go: nous avons une structure avec un nom et une liste de gardiens, et 3 événements: nous pouvons ajouter un nouveau dog sitter, nous pouvons en supprimer un et notre chien peut émettre des événements.

// 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) } } }

Courir notre chien autour

Prenons le chien dans le monde et voyons comment cela fonctionne. D'abord, je suis le seul à m'occuper du chien, alors je vais m'ajouter comme gardien de chien avecbalto.AddSitter()pour tous les événements que je peux gérer:bark,poop,hungry.

Au bout d'un moment, je suis devenu fatigué de ramasser la merde et j'ai embauché un dogitter pour faire le travail à ma place, mais je vais quand même dire au chien de ne pas aboyer et de le nourrir.

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">&lt;-</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">&lt;-</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>()

}

j'utilisefmt.Scanln()à la fin pour empêcher le programme de se terminer lorsquemain()est terminé, en attendant que les événements de gestion des goroutines se terminent.

Si je le dirige, il semble que notre nouvelle recrue fasse aussi le travail que nous attendons de lui, et nous pouvons nous détendre:

$ 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, wont 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!


Plus de tutoriels go: