Go Design Patterns : Observer
Tags: beginners, go, programming, productivity
Published: 2022-06-02 01:36:05

![Factory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4qwjyv9vl7fmx3xnq8h.png)

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.

```go
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

```go
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

```go
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

```go
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.