DEV Community

Cover image for Never Miss an Update: Master the Observer Pattern in Go with Real-World Examples
Archit Agarwal
Archit Agarwal

Posted on

Never Miss an Update: Master the Observer Pattern in Go with Real-World Examples

Why Should You Care About the Observer Pattern?

Have you ever felt the FOMO when you miss out on an important update? Imagine a world where you must constantly refresh your email to check for new messages. Sounds painful, right? ๐Ÿ˜ฉ

Well, thatโ€™s exactly how inefficient polling works in software. Instead of constantly checking, wouldnโ€™t it be better if you just got notified automatically when something changed?

Thatโ€™s where the Observer Pattern comes in! This pattern lets different parts of your system subscribe to updates and automatically receive notifications when an event occurs. Itโ€™s the backbone of event-driven programming and is used in real-world systems like:

โœ… Live sports score updates
โœ… Stock market price alerts
โœ… Job portal notifications
โœ… E-commerce product price drop alerts
โœ… Hashtag-based content subscription (which weโ€™ll build today!)

In this article, weโ€™ll demystify the Observer Pattern and implement a Hashtag Subscription System in Golang, where users can subscribe to specific hashtags and receive notifications when new articles are published.

What is the Observer Pattern?

The Observer Pattern is a behavioural design pattern where:

  • A Subject (Publisher) maintains a list of Observers (Subscribers).
  • Whenever the subjectโ€™s state changes, it notifies all registered observers automatically.
  • Observers can subscribe or unsubscribe dynamically without affecting the subjectโ€™s core logic. This pattern decouples objects, making systems more modular and scalable.

Implementing the Observer Pattern in Go

Letโ€™s build a Hashtag Subscription System, where:

๐Ÿ“Œ Users subscribe to hashtags theyโ€™re interested in.
๐Ÿ“Œ When an article is published under a hashtag, subscribers get notified.
๐Ÿ“Œ Users can unsubscribe from hashtags at any time.
๐Ÿ“Œ Users can block specific authors if they donโ€™t want to see their content.

Step 1: Define the Observer Interface (Users)

package publisher

import "fmt"

// Observer Interface (Subscribers)
type IHashtagObserver interface {
 Notify(articleID, author string)
}
Enter fullscreen mode Exit fullscreen mode

Each user who wants to receive hashtag updates must implement the Notify method.

Step 2: Define the Subject (Hashtagย Manager)

// Hashtag acts as a subject (Publisher)
type Hashtag struct {
 observers map[string]IHashtagObserver
 name      string
 articles  map[string]bool
}

// Register an observer (User)
func (tag *Hashtag) Register(name string, observer IHashtagObserver) {
 tag.observers[name] = observer
}

// Deregister an observer (User)
func (tag *Hashtag) Deregister(name string) {
 delete(tag.observers, name)
}

// Publish a new article
func (tag *Hashtag) NewArticlePublished(articleID, author string) {
 fmt.Printf("\n๐Ÿ“ข Notifying subscribers of #%s about new article (ID: %s) by %s\n", tag.name, articleID, author)
 tag.notify(articleID, author)
}

// Notify all observers
func (tag *Hashtag) notify(articleID, author string) {
 for _, observer := range tag.observers {
  observer.Notify(articleID, author)
 }
}
Enter fullscreen mode Exit fullscreen mode

The Hashtag Manager maintains a list of subscribers and notifies them when a new article is published.

Step 3: Implement the Concrete Observerย (User)

// User struct acts as an Observer
type User struct {
 id            string
 name          string
 email         string
 slackUserID   string
 hashtags      map[string]bool
 blockedAuthor map[string]bool
}

// Add an author to the blocked list
func (u *User) AddBlockedAuthor(author string) {
 u.blockedAuthor[author] = true
}

// Remove an author from the blocked list
func (u *User) RemoveBlockedAuthor(author string) {
 delete(u.blockedAuthor, author)
}

// Subscribe to a hashtag
func (u *User) AddHashtag(tagName string) {
 u.hashtags[tagName] = true
 publisher.GetHashtag(tagName).Register(u.name, u)
}

// Unsubscribe from a hashtag
func (u *User) RemoveHashtag(tagName string) {
 delete(u.hashtags, tagName)
 publisher.GetHashtag(tagName).Deregister(u.name)
}

// Receive notifications for new articles
func (u *User) Notify(articleID, author string) {
 if u.blockedAuthor[author] {
  fmt.Printf("๐Ÿšซ %s skipped notification for article (ID: %s) - blocked author: %s\n", u.name, articleID, author)
  return
 }
 fmt.Printf("\n๐Ÿ“จ Sending notification to %s\n", u.name)
 if u.email != "" {
  fmt.Printf("๐Ÿ“ง Email sent to %s for new article (ID: %s) by %s\n", u.email, articleID, author)
 }
 if u.slackUserID != "" {
  fmt.Printf("๐Ÿ’ฌ Slack notification sent to %s for new article (ID: %s) by %s\n", u.slackUserID, articleID, author)
 }
}
Enter fullscreen mode Exit fullscreen mode

Each user can subscribe/unsubscribe from hashtags dynamically!

Step 4: Bringing It Allย Together

func main() {
 var (
  tagCleanCode           = "Clean Code"
  tagSoftwareDevelopment = "Software Development"
 )
 publisher.AddNewHashtag(tagSoftwareDevelopment)
 publisher.AddNewHashtag(tagCleanCode)

 // Create users and subscribe them to hashtags
 user.InitUser("1", "Alice", "alice@example.com", "alice_slack", []string{tagSoftwareDevelopment}, nil)
 user.InitUser("2", "Bob", "bob@example.com", "bob_slack", []string{tagSoftwareDevelopment, tagCleanCode}, nil)
 user3 := user.InitUser("3", "Charlie", "charlie@example.com", "charlie_slack", []string{tagSoftwareDevelopment, tagCleanCode}, []string{"author1"})

 // Publish new articles under hashtags
 publisher.GetHashtag(tagSoftwareDevelopment).NewArticlePublished("article1", "author2")
 publisher.GetHashtag(tagSoftwareDevelopment).NewArticlePublished("article2", "author1") // Charlie blocked author1
 publisher.GetHashtag(tagCleanCode).NewArticlePublished("article3", "author1")

 // Charlie unsubscribes from "Clean Code" hashtag
 user3.RemoveHashtag(tagCleanCode)
 publisher.GetHashtag(tagCleanCode).NewArticlePublished("article4", "author2") // Charlie won't be notified
}
Enter fullscreen mode Exit fullscreen mode

Expected Output

๐Ÿ“ข Notifying subscribers of #Software Development about new article (ID: article1) by author2

๐Ÿ“จ Sending notification to Alice
๐Ÿ“ง Email sent to alice@example.com for new article (ID: article1) by author2
๐Ÿ’ฌ Slack notification sent to alice_slack for new article (ID: article1) by author2

๐Ÿ“จ Sending notification to Bob
๐Ÿ“ง Email sent to bob@example.com for new article (ID: article1) by author2
๐Ÿ’ฌ Slack notification sent to bob_slack for new article (ID: article1) by author2

๐Ÿ“จ Sending notification to Charlie
๐Ÿ“ง Email sent to charlie@example.com for new article (ID: article1) by author2
๐Ÿ’ฌ Slack notification sent to charlie_slack for new article (ID: article1) by author2

๐Ÿ“ข Notifying subscribers of #Software Development about new article (ID: article2) by author1

๐Ÿ“จ Sending notification to Alice
๐Ÿ“ง Email sent to alice@example.com for new article (ID: article2) by author1
๐Ÿ’ฌ Slack notification sent to alice_slack for new article (ID: article2) by author1

๐Ÿ“จ Sending notification to Bob
๐Ÿ“ง Email sent to bob@example.com for new article (ID: article2) by author1
๐Ÿ’ฌ Slack notification sent to bob_slack for new article (ID: article2) by author1
๐Ÿšซ Charlie skipped notification for article (ID: article2) - blocked author: author1

๐Ÿ“ข Notifying subscribers of #Clean Code about new article (ID: article3) by author1

๐Ÿ“จ Sending notification to Bob
๐Ÿ“ง Email sent to bob@example.com for new article (ID: article3) by author1
๐Ÿ’ฌ Slack notification sent to bob_slack for new article (ID: article3) by author1
๐Ÿšซ Charlie skipped notification for article (ID: article3) - blocked author: author1

๐Ÿ“ข Notifying subscribers of #Clean Code about new article (ID: article4) by author2

๐Ÿ“จ Sending notification to Bob
๐Ÿ“ง Email sent to bob@example.com for new article (ID: article4) by author2
๐Ÿ’ฌ Slack notification sent to bob_slack for new article (ID: article4) by author2
Enter fullscreen mode Exit fullscreen mode

Boom! ๐ŸŽ‰ Alice and Bob got their notifications instantly and Charlie also was able to block a particular author! No polling, no wasted CPU cycles. Just clean, efficient event-driven programming.

You can find this complete working code on my Github.

Key Takeaways

โœ… The Observer Pattern helps create event-driven, decoupled systems.
โœ… It's used in stock updates, job alerts, social media notifications, and more.
โœ… Our Hashtag Subscription System shows how users can subscribe, unsubscribe, and receive notifications in real-time.

Final Thoughts

This is just the tip of the iceberg. You can extend this pattern for WebSockets, message brokers, or even real-time dashboards! ๐Ÿš€

Stay Connected!

๐Ÿ’ก Follow me here on LinkedIn for more insights on software development and architecture.
๐ŸŽฅ Subscribe to my YouTube channel for in-depth tutorials.
๐Ÿ“ฌ Sign up for my newsletter, The Weekly Golang Journal, for exclusive content.
โœ๏ธ Follow me on Medium for detailed articles.
๐Ÿ‘จโ€๐Ÿ’ป Join the discussion on my subreddit, r/GolangJournal, and be part of the community!

Top comments (0)