Observer pattern in Go language
This problem is pretty common: an object should notify all its subscribers when some event occurs. In C++ we may use boost::signals
or something else. But how to do this in Go language? It would be nice to see some working code example where a couple of ob开发者_如何学Gojects are subscribed to a publisher and process notifications.
Thanks
This is actually pretty simple in Go. Use channels. This is the kind of thing they're made for.
type Publish struct {
listeners []chan *Msg
}
type Subscriber struct {
Channel chan *Msg
}
func (p *Publisher) Sub(c chan *Msg) {
p.appendListener(c)
}
func (p *Publisher) Pub(m *Msg) {
for _, c := range p.listeners {
c <- Msg
}
}
func (s *Subscriber) ListenOnChannel() {
for {
data := <-s.Channel
//Process data
}
}
func main() {
for _, v := range subscribers {
p.Sub(v.Channel)
go v.ListenOnChannel()
}
//Some kind of wait here
}
Obviously this isn't exactly a working code sample. But it's close.
Here I give a classific implementation without channels, be free to refer this post
Assumed Example: Suppose you are interested in the stock market. You have the following needs: You want to keep track of the stock prices of a particular company (e.g. Apple Inc). You would not like to miss any stock price update especially if the price is dropping to a certain point. You would like to be notified of all the stock price updates.
interfaces:
// Subject interface
type Subject interface {
Attach(o Observer) (bool, error)
Detach(o Observer) (bool, error)
Notify() (bool, error)
}
// Observer Interface
type Observer interface {
Update(string)
}
Concrete Observer object
// Concrete Observer: StockObserver
type StockObserver struct {
name string
}
func (s *StockObserver) Update(t string) {
// do something
println("StockObserver:", s.name, "has been updated,", "received subject string:", t)
}
Concrete Subject object
// Concrete Subject: stockMonitor
type StockMonitor struct {
// internal state
ticker string
price float64
observers []Observer
}
func (s *StockMonitor) Attach(o Observer) (bool, error) {
for _, observer := range s.observers {
if observer == o {
return false, errors.New("Observer already exists")
}
}
s.observers = append(s.observers, o)
return true, nil
}
func (s *StockMonitor) Detach(o Observer) (bool, error) {
for i, observer := range s.observers {
if observer == o {
s.observers = append(s.observers[:i], s.observers[i+1:]...)
return true, nil
}
}
return false, errors.New("Observer not found")
}
func (s *StockMonitor) Notify() (bool, error) {
for _, observer := range s.observers {
observer.Update(s.String())
}
return true, nil
}
func (s *StockMonitor) SetPrice(price float64) {
s.price = price
s.Notify()
}
func (s *StockMonitor) String() string {
convertFloatToString := strconv.FormatFloat(s.price, 'f', 2, 64)
return "StockMonitor: " + s.ticker + " $" + convertFloatToString
}
main.go
func main() {
// Create a new stockMonitor object
stockMonitor := &StockMonitor{
ticker: "AAPL",
price: 0.0,
}
observerA := &StockObserver{
name: "Observer A",
}
observerB := &StockObserver{
name: "Observer B",
}
// Attach our Observers to the stockMonitor
stockMonitor.Attach(observerA)
stockMonitor.Attach(observerB)
// Start the stockMonitor
stockMonitor.Notify()
// Change the price of the stockMonitor
stockMonitor.SetPrice(500)
// Detach an Observer from the stockMonitor
stockMonitor.Detach(observerA)
// Change the price of the stockMonitor
stockMonitor.SetPrice(528)
}
In this part
- We create two observers, observerA and observerB. Attach them to the stockMonitor.
- Change the price of the stockMonitor.
- We see that observerA and obsererB are both notified.
- Detach observerA from the stockMonitor and change the stock price. We can see that only observerB is notified.
精彩评论