Go Patterns

How to Implement Observer Pattern in Go

Behavioral Go Design Pattern

Go Observer Design Pattern

Observer Pattern

The Observer Pattern defines a one-to-many dependency between Notifier and Observers. When Notifier changes its state all Observers are notified with Events.

This pattern can be used in the event-driven code. When one parts of the code have to react on changes in another part.

Basic Types and Interfaces

We will start with defining type for Event.

// Event defines an indication of a some occurence
type Event struct {
	// Data in this case is a simple int.
	Data int
}

Next step will be to write an interface for Observer.

// Observer defines a standard interface
// to listen for a specific event.
type Observer interface {
	// OnNotify allows to publsh an event
	OnNotify(Event)
}

Third step is to implement interface for the Notifier.

This interface has 3 methods:

// Notifier is the instance being observed.
type  Notifier interface {
	// Register itself to listen/observe events.
	Register(Observer)
	// Remove itself from the collection of observers/listeners.
	Unregister(Observer)
	// Notify publishes new events to listeners.
	Notify(Event)
}

Implement Concrete Types

Now let’s implement concrete Observer using our Observer interface.

type observer struct {
	id int
}

func (o *observer) OnNotify(e Event) {
	fmt.Printf(
		"observer %d recieved event %d\n",
		o.id, e.Data,
	)
}

Next step is to define Notifier for our Notifier interface.

type notifier struct {
	observers map[Observer]struct{}
}

func (n *notifier) Register(o Observer) {
	n.observers[o] = struct{}{}
}

func (n *notifier) Unregister(o Observer) {
	delete(n.observers, o)
}

func (n *notifier) Notify(e Event) {
	for o := range n.observers {
		o.OnNotify(e)
	}
}

Usage Example

Get all things together and write the main function.

package main

func main() {
	n := notifier{
  	observers: map[Observer]struct{}{},
	}

	n.Register(&observer{1})
	n.Register(&observer{2})

	n.Notify(Event{1})
	n.Notify(Event{101})
	n.Notify(Event{9999})
}

And output will be

# Output
  go-patterns go run ./behavioral/observer
observer 1 recieved event 1
observer 2 recieved event 1
observer 1 recieved event 101
observer 2 recieved event 101
observer 1 recieved event 9999
observer 2 recieved event 9999

Conclusion

Implementing a basic Observer Pattern is not a hard task, and it is often implemented in the event-driven code. In those systems, the Notifier is usually named a “stream of events”.

Happy coding!

Code examples can be found in my GitHub repo pavel-fokin/go-patterns.