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: %vn", 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: %vn", 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:
Example | Source Code Link |
---|---|
Example #1: General Observer | GitHub |
Other Code Implementations
Use the following links to check Observer pattern implementation in other programming languages.