Patterns: behavior · 3 MIN READ
Observer: subscribe, notify and unsubscribe
Keep notification lifetime and delivery semantics explicit.
Keep notification lifetime and delivery semantics explicit. Observer lets interested listeners subscribe to changes without the subject knowing their concrete types. Trace the worked Go example, then compare the benefit with the cost before applying this design to your own project.
Behavioral pattern · A MAP OF THE IDEA
- Listener subscribes to a subject
- Subject notifies registered listeners
- Owner unsubscribes at end of lifetime
Useful when
- Several local components react to changes
- The subject should not know listener types
Watch out
- Slow callbacks can block the producer
- Subscriptions can retain objects unexpectedly
Implementation & operation cost: Medium
Fit depends on the task
Editorial suitability, not benchmarks or a universal ranking. Styles describe different axes and can be combined.
The design decision
Observer lets interested listeners subscribe to changes without the subject knowing their concrete types. Local synchronous callbacks are often enough. This is not a durable event bus: a crashed process loses listeners and an in-process notification has no automatic redelivery guarantee.
A complete Go example
Read this program from the entry point and follow the calls shown in the diagram. The output is included at the end so you can check the behavior without a compiler.
package main
import (
"fmt"
)
type Subject struct {
listeners map[int]func(int)
next int
}
func (s *Subject) Subscribe(f func(int)) func() {
id := s.next
s.next++
s.listeners[id] = f
return func() { delete(s.listeners, id) }
}
func (s *Subject) Notify(n int) {
for _, f := range s.listeners {
f(n)
}
}
func main() {
s := Subject{listeners: make(map[int]func(int))}
total := 0
stop := s.Subscribe(func(n int) { total += n })
s.Notify(3)
stop()
s.Notify(5)
fmt.Println(total)
}
// Output:
// 3
The subscription returns its own cleanup operation. After stop, a later notification has no effect on total. The example assumes a single owner, no reentrant map mutation and listeners whose order does not matter.
Tradeoffs and the simpler alternative
Decide whether delivery is synchronous, ordered, droppable or buffered before introducing goroutines. Slow or panicking listeners can affect the subject. Retained callbacks can also retain large object graphs, so unsubscribe on the owner’s lifecycle boundary.
TAKE THIS WITH YOU
Needs explicit delivery and unsubscribe semantics. Decide whether delivery is synchronous, ordered, droppable or buffered before introducing goroutines. Slow or panicking listeners can affect the subject. Retained callbacks can also retain large object graphs, so unsubscribe on the owner’s lifecycle boundary.
Check your understanding
Which statement best describes Observer: subscribe, notify and unsubscribe?
Go to the source
Progress stays in this browser. No account needed.