Early previewThis guide is in development. Items tagged verify are still being checked against official docs.
nebx Learn
GitHub
Menu
Go

Go

Simple, fast to compile, built for concurrent network services.

Paradigm
Procedural, composition over inheritance
Typing
Static, strong, structural interfaces
Memory
Concurrent low-latency GC
Runs
Single static native binary
Concurrency
Goroutines and channels
Behavioral pattern

Observer

Intent

Defines a subscription mechanism that notifies multiple objects about events in the object they observe.

Problem

Customers keep polling the store for a new product, or the store spams everyone.

Solution

The publisher keeps a list of subscribers and notifies them when something happens. Events, signals and pub/sub are all Observer.

Real-world analogy

Think of it like this

A magazine subscription: new issues arrive when they're published, so you don't keep checking the shop.

Structure

association / extends implements / depends on
Publisher - subscribers: Subscriber[]- mainState+ subscribe(s)+ unsubscribe(s)+ notifySubscribers() 12 «interface» Subscriber + update(context) 3 ConcreteSubscribers + update(context) 4 Client 5

Hover or tap a numbered role to highlight it in the diagram.

  1. 1Publisher — Publisher issues events and keeps the subscription list.
  2. 2Publisher — When an event happens, it loops over subscribers and calls update().
  3. 3Subscriber — Subscriber declares the notification interface, usually a single update method.
  4. 4ConcreteSubscribers — Concrete Subscribers react to notifications.
  5. 5Client — Client creates publishers and subscribers and registers subscriptions.

Try it

InteractiveToggle subscribers, then publish an event. Only subscribed objects are notified.
OrderService EmailServiceInventoryAnalytics
Subscribers
// notifications appear here

Code example

observer.go
type Handler func(event string)

type Subject struct {
	mu       sync.Mutex
	handlers []Handler
}

func (s *Subject) Subscribe(h Handler) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.handlers = append(s.handlers, h)
}

func (s *Subject) Notify(event string) {
	s.mu.Lock()
	hs := append([]Handler(nil), s.handlers...)   // copy, then call outside the lock
	s.mu.Unlock()
	for _, h := range hs {
		h(event)
	}
}

Applicability

Use Observer when changes in one object require changes in others, unknown in advance.

Use Observer when some objects should observe others only for a time.

How to implement

  1. Split business logic into a publisher and subscribers
  2. Declare the subscriber interface (often a callback type)
  3. Add subscribe / unsubscribe to the publisher
  4. Notify subscribers when the event happens
  5. Subscribers register with publishers

Pros and cons

Pros

  • Open–Closed: new subscribers without changing the publisher
  • Relationships set up at runtime

Cons

  • Notification order is not guaranteed
  • Forgotten subscriptions can leak memory

Relations with other patterns

Check yourself

Check yourself

When an order is paid, the email, inventory and analytics modules must react, and more may be added later.

Saved in this browser only. Part of Behavioral patterns (0/11), Hands-on: patterns with live demos (0/5).