Observer pattern in Go language
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.
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.