Design Pattern: Observer Pattern in Go

Observer pattern is used to set up one or more notifiers/publishers, and some subscribers for those notifiers. So that the subscribers get the change info when the notifier triggers a change event.

The notifiers are subscribers are decoupled from each other, so the subscriber does not need to know how many notifiers/publishers are there.

This article demonstrates Observer pattern implementations in Golang. Check the following examples.

Implementation

  • Create an interface for the subject. Declare methods for adding a observer, and notifying the observers.
  • Create a struct as concrete subject. Define some states(as per requirement), and a slice of type of observer interface for maintaining a list of observers. Implement the subject interface for this struct.
  • Define an interface for the observers, also define a struct for the observers (this will be embedded into the observers).
  • Define observers, and in each observer embed the observer struct and implement the observer interface.

Examples

Here are a few examples of Observer pattern implementation

Example #1: General Observer

This is a general implementation of the Observer pattern. Let’s check step-by-step.

Subject Interface

  • Create file “subject.go”.
  • Create interface “Subject”.
  • Declare methods – “Attach”, “GetState”, “NotivyObservers”, “SetState”.
// subject.go

package main

type Subject interface {
	Attach(observer IObserver)
	GetState() int
	NotifyObservers()
	SetState(state int)
}

Concrete Subject

  • Create file “concrete_subject.go”.
  • Create struct “ConcreteSubject”.
  • Define field “state” for storing some state. There can be more states of different names and types as per requirement of the subject.
  • Define a slice of type “IObserver”(the observer interface). This will be used to store and maintain a list of observers.
  • Create method “NewConcreteSubject”, for initiating a new struct.
  • Implement “Subject” interface for the struct. Define methods – “Attach”, “GetState”, “NotifyObservers”, “SetState” for the interface implementation.
  • In the “Attach” method implementation append a new observer to the “observerList”. 
  • In “GetState” method implementation return the “state”.
  • In the “NotifyObservers” method implementation, loop through the list of observers(observerList) and call the “SendUpdate” method of the observers.
  • In the “SetState” method implementation, set the state value and then call the “NotifyObservers” to inform the observers about the state change.
// concrete_subject.go

package main

type ConcreteSubject struct {
	state        int
	observerList []IObserver
}

func NewConcreteSubject() (concreteSubject *ConcreteSubject) {
	concreteSubject = &ConcreteSubject{}
	return
}

func (concreteSubject *ConcreteSubject) Attach(observer IObserver) {
	concreteSubject.observerList = append(concreteSubject.observerList, observer)
}

func (concreteSubject *ConcreteSubject) GetState() int {
	return concreteSubject.state
}

func (concreteSubject *ConcreteSubject) NotifyObservers() {
	for _, observer := range concreteSubject.observerList {
		observer.SendUpdate()
	}
}

func (concreteSubject *ConcreteSubject) SetState(state int) {
	concreteSubject.state = state
	concreteSubject.NotifyObservers()
}

Abstract Observer [Interface + Struct]

  • Create file “observer.go”.
  • Create interface “IObserver” and declare method “SendUpdate”.
  • Create struct “Observer” and define a field “subject” of type “Subject”.
// observer.go

package main

type IObserver interface {
	SendUpdate()
}

type Observer struct {
	subject Subject
}

Observer One

  • Create file “observer_one.go”.
  • Define struct “ObserverOne”.
  • Embed the “Observer” struct into “ObserverOne”.
  • Create method “NewObserverOne”, that accepts a subject param. Initiate a new “ObserverOne” struct, set the subject, and “Attach” the “observerOne” to the subject.
  • Implement the interface “IObserver” for the struct. Define the “SendUpdate” method as part of the interface implementation.
// observer_one.go

package main

import "fmt"

type ObserverOne struct {
	Observer
}

func NewObserverOne(subject Subject) (observerOne *ObserverOne) {
	observerOne = &ObserverOne{}
	observerOne.subject = subject
	observerOne.subject.Attach(observerOne)
	return
}

func (observerOne *ObserverOne) SendUpdate() {
	fmt.Printf("Received in ObserverOne: %v\n", observerOne.subject.GetState())
}

Observer Two

  • Create file “observer_two.go”.
  • Define struct “ObserverTwo”.
  • Embed “Observer” into “ObserverTwo”.
  • Create method “NewObserverTwo” for initializing a new struct.
  • Implement the interface “IObserver” for the struct by defining the method “SendUpdate”.
// observer_two.go

package main

import "fmt"

type ObserverTwo struct {
	Observer
}

func NewObserverTwo(subject Subject) (observerTwo *ObserverTwo) {
	observerTwo = &ObserverTwo{}
	observerTwo.subject = subject
	observerTwo.subject.Attach(observerTwo)
	return
}

func (observerTwo *ObserverTwo) SendUpdate() {
	fmt.Printf("Received in ObserverTwo: %v\n", observerTwo.subject.GetState())
}

Demo

For using the implementation, create a new “ConcreteSubject” and store that in “subject” variable.

Initiate observers by calling “NewObserverOne” and “NewObserverTwo”, and pass the “subject” as param, so that the observer can be attached to the subject.

Call the “setState” on the subject. This state set/change will trigger a notification and all the observers will be notified.

// main.go

package main

import "fmt"

func main() {
	subject := NewConcreteSubject()

	NewObserverOne(subject)
	NewObserverTwo(subject)
	fmt.Println("Setting subject value to 10")
	subject.SetState(10)

	fmt.Println("Setting subject value to 999")
	subject.SetState(999)
}

Output

Folowing output will be generated from the demo code.

Setting subject value to 10
Received in ObserverOne: 10
Received in ObserverTwo: 10


Setting subject value to 999
Received in ObserverOne: 999
Received in ObserverTwo: 999

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check Observer pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.