在“通过通道”中实现事件侦听器

这篇文章旨在解释如何使用Go通道实现事件侦听器,即所谓的设计模式术语中的观察者。

问题

我有一只有时会吠叫的狗,这种情况发生时,我通常会告诉他不要吠叫。和所有的狗一样,他有时会大便,所以我需要捡起来。

我将把我的狗建模为具有事件侦听器列表的结构,这是照顾他的人的列表。

当事情发生时,狗会向那些听众发出事件。

这是我们在Go中为狗建模的方式:我们有一个带有名称和看护者列表的结构,以及3个事件:我们可以添加一个新的保姆,可以删除一个保姆,并且我们的狗可以发出事件。

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

到处乱跑我们的狗

让我们将狗带入世界,看看它是如何工作的。首先,我是唯一照顾狗的人,所以我将自己添加为狗的保姆balto.AddSitter()对于我可以处理的所有事件:barkpoophungry

一段时间后,我厌倦了捡便便,并雇了一个保姆去为我做事,但我仍然会告诉狗不要吠叫并喂他。

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>()

}

我正在使用fmt.Scanln()最后防止程序退出时main()已经结束,同时仍在等待goroutines处理事件完成。

如果我管理它,看来我们的新员工也在做我们期望他做的工作,我们可以放松一下:

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


更多教程: