golang观察者模式

304 阅读1分钟

相关repo: gitee.com/gaaidou/gaa…

观察者模式:

观察者模式.jpg

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