It is a behavior pattern that allows us to have a group of objects (observers) subscribed to another object (observable).
It is usually used to notify observers of the occurrence of an event.
The Goroutines provide us an quite efficient way to implement this design pattern.
Let's see this example.
- First, we need to implement our observable object, this will have an array of observers.
In this example our observable object will be a "Topic" this will notify his observers of the availability of a product.
type Topic struct {
Observers []Observer
Name string
Available bool
}
//Constructor
func NewTopic(name string) *Topic {
return &Topic{
Name: name,
}
}
//Update 'Available' value and notify it to Observers
func (i *Topic) UpdateAvailable(value bool) {
i.Available = value
i.Broadcast()
}
//Using Goroutines, will notify each observer of the array
func (i *Topic) Broadcast() {
var wg sync.WaitGroup
for _, obj := range i.Observers {
wg.Add(1)
go func(observer Observer) {
defer wg.Done()
observer.Notify(i.Name, i.Available)
}(obj)
}
wg.Wait()
}
//Add a new observer to the array
func (i *Topic) Register(observer Observer) {
i.Observers = append(i.Observers, observer)
}
- Then we need to define how "looks" our observer objects, this objects can have differents behaviors, in OOP we would usually use an abstract class for the Observer, in Go we can use an Interface where each object needs to implement in order to be an Observer.
In this example exists to types of Observers, EmailClient and SMSClient
type Observer interface {
GetId() string //Id Getter
Notify(name string, value bool) //Send the notification
}
type EmailClient struct {
id string
}
func (eC *EmailClient) GetId() string {
return eC.id
}
func (eC *EmailClient) Notify(name string, value bool) {
if value {
fmt.Printf("Sending email - %s available to client %s\n", name, eC.id)
} else {
fmt.Printf("Sending email - %s not available to client %s\n", name, eC.id)
}
}
type SMSClient struct {
id string
}
func (sC *SMSClient) GetId() string {
return sC.id
}
func (sC *SMSClient) Notify(name string, value bool) {
if value {
fmt.Printf("Sending SMS - %s available to client %s\n", name, sC.id)
} else {
fmt.Printf("Sending SMS - %s not available to client %s\n", name, sC.id)
}
}
Running Our Code
func main() {
nvidiaTopic := NewTopic("RTX 3080")
firstObserver := &EmailClient{
id: "100",
}
secondObserver := &SMSClient{
id: "200",
}
nvidiaTopic.Register(firstObserver)
nvidiaTopic.Register(secondObserver)
nvidiaTopic.UpdateAvailable(true)
nvidiaTopic.UpdateAvailable(false)
}
Output
Sending SMS - RTX 3080 available to client 200
Sending email - RTX 3080 available to client 100
Sending SMS - RTX 3080 not available to client 200
Sending email - RTX 3080 not available to client 100
Program exited.
Full Example
package main
import (
"fmt"
"sync"
)
type Topic struct {
Observers []Observer
Name string
Available bool
}
//Constructor
func NewTopic(name string) *Topic {
return &Topic{
Name: name,
}
}
//Update 'Available' value and notify it to Observers
func (i *Topic) UpdateAvailable(value bool) {
i.Available = value
i.Broadcast()
}
//Using Goroutines, will notify each observer of the array
func (i *Topic) Broadcast() {
var wg sync.WaitGroup
for _, obj := range i.Observers {
wg.Add(1)
go func(observer Observer) {
defer wg.Done()
observer.Notify(i.Name, i.Available)
}(obj)
}
wg.Wait()
}
//Add a new observer to the array
func (i *Topic) Register(observer Observer) {
i.Observers = append(i.Observers, observer)
}
type Observer interface {
GetId() string //Id Getter
Notify(name string, value bool) //Send the notification
}
type EmailClient struct {
id string
}
func (eC *EmailClient) GetId() string {
return eC.id
}
func (eC *EmailClient) Notify(name string, value bool) {
if value {
fmt.Printf("Sending email - %s available to client %s\n", name, eC.id)
} else {
fmt.Printf("Sending email - %s not available to client %s\n", name, eC.id)
}
}
type SMSClient struct {
id string
}
func (sC *SMSClient) GetId() string {
return sC.id
}
func (sC *SMSClient) Notify(name string, value bool) {
if value {
fmt.Printf("Sending SMS - %s available to client %s\n", name, sC.id)
} else {
fmt.Printf("Sending SMS - %s not available to client %s\n", name, sC.id)
}
}
func main() {
nvidiaTopic := NewTopic("RTX 3080")
firstObserver := &EmailClient{
id: "100",
}
secondObserver := &SMSClient{
id: "200",
}
nvidiaTopic.Register(firstObserver)
nvidiaTopic.Register(secondObserver)
nvidiaTopic.UpdateAvailable(true)
nvidiaTopic.UpdateAvailable(false)
}
And that's all, see you for another post.
Top comments (0)