Skip to content

Instantly share code, notes, and snippets.

@patrickmn
Created January 2, 2012 09:12
Show Gist options
  • Save patrickmn/1549985 to your computer and use it in GitHub Desktop.
Save patrickmn/1549985 to your computer and use it in GitHub Desktop.
Go observer pattern example
package main
import (
"fmt"
"sync"
"time"
)
var wg *sync.WaitGroup
type Observable struct {
observers []chan int
mu *sync.Mutex
}
func (o *Observable) Attach(c chan int) {
o.mu.Lock()
defer o.mu.Unlock()
o.observers = append(o.observers, c)
}
func (o *Observable) Detach(c chan int) {
o.mu.Lock()
defer o.mu.Unlock()
for i, v := range o.observers {
if v == c {
o.observers = append(o.observers[:i], o.observers[i+1:]...)
return
}
}
}
func (o *Observable) Notify(evt int) {
for _, v := range o.observers {
v <- evt
}
}
type Observer struct {
Food string
ch chan int
}
func (obs *Observer) Observe() {
evt := <-obs.ch
fmt.Println("Food:", obs.Food, "Event:", evt)
wg.Done()
}
func main() {
o := &Observable{}
o.mu = &sync.Mutex{}
obs := []Observer{
{"Eggs", make(chan int, 2)},
{"Bacon", make(chan int, 2)},
}
wg = &sync.WaitGroup{}
wg.Add(len(obs))
for _, v := range obs {
o.Attach(v.ch)
go v.Observe()
}
go func() {
<-time.After(1 * time.Second)
o.Notify(3)
}()
go func() {
<-time.After(3 * time.Second)
o.Notify(5)
}()
wg.Wait()
}
@chaudharisuresh997
Copy link

Hi Patrick,
Its a great code to implement observer with go channels.
I heard you are running a startup what services your product provides.
Thanks
Suresh

@eduardonunesp
Copy link

A little different approach

package main

import (
	"fmt"
	"sync"
)

type (
	Observable interface {
		Add(observer Observer)
		Notify(event interface{})
		Remove(event interface{})
	}

	Observer interface {
		NotifyCallback(event interface{})
	}

	WatchTower struct {
		observer sync.Map
	}

	Soldier struct {
		id   int
		zone string
	}
)

func (wt *WatchTower) Add(observer Observer) {
	wt.observer.Store(observer, struct{}{})
}

func (wt *WatchTower) Remove(observer Observer) {
	wt.observer.Delete(observer)
}

func (wt *WatchTower) Notify(event interface{}) {
	wt.observer.Range(func(key, value interface{}) bool {
		if key == nil {
			return false
		}

		key.(Observer).NotifyCallback(event)
		return true
	})
}

func (s Soldier) NotifyCallback(event interface{}) {
	if event.(string) == s.zone {
		fmt.Printf("Soldier %d, seen an enemy on zone %s\n", s.id, event)
	}
}

func main() {
	watchTower := WatchTower{}
	soldier_1 := Soldier{id: 1, zone: "B"}
	soldier_2 := Soldier{id: 2, zone: "A"}

	watchTower.Add(soldier_1)
	watchTower.Add(soldier_2)

	// Notify Zone A
	watchTower.Notify("A")

	// Notify Zone B
	watchTower.Notify("B")

	// Remove soldier 1 (No soldier on Zome B anymore)
	watchTower.Remove(soldier_1)

	// Notify Zone B (Enemy is free to pass here)
	watchTower.Notify("B")
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment