相关repo: gitee.com/gaaidou/gaa…
观察者模式:
Go语言的Publish-Subscribe模式可以使用Go自身的channel特性很好地表示,由于channel不能实现boardcast模式,因此每个Observer都会有一个channel对应
observer.go
package designpattern
import (
"fmt"
"sync"
)
type Subject struct {
registryMap map[string]*Observer
lock sync.Mutex
}
func NewSubject() *Subject {
return &Subject{registryMap: make(map[string]*Observer)}
}
func (s *Subject) Registry(name string) error {
s.lock.Lock()
defer s.lock.Unlock()
if _, ok := s.registryMap[name]; !ok {
s.registryMap[name] = newObserver(name)
return nil
} else {
return fmt.Errorf("%s exists", name)
}
}
func (s *Subject) Unregistry(name string) error {
s.lock.Lock()
defer s.lock.Unlock()
if v, ok := s.registryMap[name]; ok {
v.destory()
delete(s.registryMap, name)
return nil
} else {
return fmt.Errorf("%s does not exists", name)
}
}
func (s *Subject) Notify(news string) {
for _, v := range s.registryMap {
v.news <- news
}
}
type Observer struct {
news chan string
name string
}
func (o *Observer) listen() {
for {
select {
case news, ok := <-o.news:
if ok {
fmt.Printf("%s receive news: %s\n", o.name, news)
} else {
return
}
}
}
}
func (o *Observer) destory() {
close(o.news)
}
func newObserver(name string) *Observer {
observer := &Observer{name: name, news: make(chan string)}
go observer.listen()
return observer
}
observer_test.go
package design_pattern
import (
"ptarmigan-golang-design-pattern/src/designpattern"
"testing"
"time"
)
func TestObserver(t *testing.T) {
subject := designpattern.NewSubject()
subject.Registry("Nick")
subject.Registry("John")
subject.Registry("Martin")
subject.Registry("Lisa")
subject.Notify("Successfully!!!")
time.Sleep(time.Second * 3)
subject.Unregistry("Lisa")
subject.Notify("Lisa don't receive news!")
time.Sleep(time.Second * 3)
}