Understanding the Observer Pattern with Go Code Examples
This article explains the Observer (publish‑subscribe) pattern, illustrates it with a humorous dialogue, provides complete Go interface and struct implementations for subjects and observers, shows sample client code and output, and discusses practical refactoring using the pattern for configuration changes.
The Observer Pattern (also known as publish/subscribe) defines a one‑to‑many dependency where a Subject holds references to multiple Observers that automatically receive state‑change events.
To make the concept concrete, the article presents a dialogue that likens everyday app notifications to observers receiving updates from various platforms.
It then defines the abstract interfaces in Go:
type Platform string
const (
WEIBO Platform = "weibo"
BILIBILI Platform = "bilibili"
ZHIHU Platform = "zhihu"
)
// MoYu is the abstract interface for a "moyu" (idle‑time) app
type MoYu interface {
Platform() Platform
GetName() string
GetContent() string
}And the observer abstraction:
// Mortal represents a user (observer)
type Mortal interface {
Accept()
}
type BaseMortal struct {
MoYu
}A reusable base subject implementation is provided:
type BaseMoYu struct {
Name string
Content string
MortalList []Mortal
}
func (b *BaseMoYu) Attach(m Mortal) { b.MortalList = append(b.MortalList, m) }
func (b *BaseMoYu) Publish(name, content string) {
b.Name = name
b.Content = content
for _, item := range b.MortalList { item.Accept() }
}
func (b *BaseMoYu) GetName() string { return b.Name }
func (b *BaseMoYu) GetContent() string { return b.Content }Concrete subject for Bilibili:
type MoYuByBilibili struct { BaseMoYu }
func NewMoYuByBilibili() *MoYuByBilibili { return &MoYuByBilibili{} }
func (m *MoYuByBilibili) Platform() Platform { return BILIBILI }Two concrete observers (Tom and Jerry) are defined:
type Tom struct { BaseMortal }
func NewTom(mo MoYu) *Tom { return &Tom{BaseMortal{MoYu: mo}} }
func (t Tom) Accept() {
fmt.Printf("Tom accept from: %v, content: %v, published by: %v\n", t.BaseMortal.Platform(), t.BaseMortal.GetContent(), t.BaseMortal.GetName())
}
type Jerry struct { BaseMortal }
func NewJerry(mo MoYu) *Jerry { return &Jerry{BaseMortal{MoYu: mo}} }
func (j Jerry) Accept() {
fmt.Printf("Jerry accept from: %v, content: %v, published by: %v\n", j.BaseMortal.Platform(), j.BaseMortal.GetContent(), j.BaseMortal.GetName())
}Client code demonstrates attaching observers and publishing two events:
func main() {
bilibili := NewMoYuByBilibili()
tom := NewTom(bilibili)
jerry := NewJerry(bilibili)
bilibili.Attach(tom)
bilibili.Attach(jerry)
bilibili.Publish("张朝阳的物理课", "张朝阳、俞敏洪是怎样“摸鱼”的")
bilibili.Publish("哔哩哔哩国创", "《暂停!让我查攻略》")
}The program outputs lines showing Tom and Jerry receiving each published message, illustrating the one‑to‑many notification flow.
Finally, the article connects the pattern to a real‑world configuration‑change scenario using Nacos. It shows a ListenConfig call that triggers an OnChange() function, and argues that each business‑specific configuration handler can be modeled as an observer, keeping the subject (configuration center) decoupled from observers and adhering to the Open/Closed principle.
Overall, the piece teaches the Observer pattern, provides ready‑to‑run Go examples, and demonstrates how to apply the pattern to improve maintainability in practical projects.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.