Это модель поведения, которая позволяет нам иметь группу объектов (наблюдателей), подписанных на другой объект (наблюдаемый).
Обычно он используется для уведомления наблюдателей о наступлении события.
Гороутины предоставляют нам довольно эффективный способ реализации этого паттерна проектирования.
Давайте рассмотрим этот пример.
- Во-первых, нам нужно реализовать объект observable, который будет содержать массив наблюдателей.
В данном примере нашим наблюдаемым объектом будет «Topic», который будет уведомлять своих наблюдателей о наличии товара.
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)
}
- Затем нам нужно определить, как «выглядят» наши объекты-наблюдатели, эти объекты могут иметь различное поведение, в ООП мы обычно используем абстрактный класс для наблюдателя, в Go мы можем использовать интерфейс, который каждый объект должен реализовать, чтобы быть наблюдателем.
В данном примере существует несколько типов наблюдателей, EmailClient и 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 %sn", name, eC.id)
} else {
fmt.Printf("Sending email - %s not available to client %sn", 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 %sn", name, sC.id)
} else {
fmt.Printf("Sending SMS - %s not available to client %sn", 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)
}
Вывод
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.
Пример полного экрана
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 %sn", name, eC.id)
} else {
fmt.Printf("Sending email - %s not available to client %sn", 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 %sn", name, sC.id)
} else {
fmt.Printf("Sending SMS - %s not available to client %sn", 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)
}
Вот и все, до встречи в следующем посте.