понедельник, 14 октября 2019 г.

Паттерны в Golang: Observer (Наблюдатель)

Паттерн Observer (Наблюдатель) позволяет экземпляру типа "публиковать" события для других экземпляров типа ("наблюдателей"), которые хотят быть обновленными, когда происходит определенное событие.

Реализация

В долго работающих приложениях, таких как экземпляры веб-серверов, можно хранить коллекцию наблюдателей, которые будут получать уведомления о запущенных событиях.

Реализации различаются, но интерфейсы могут использоваться для создания стандартных наблюдателей (observers) и уведомителей (notifiers):

type (
  // Event определяет индикацию 
  // возникновения момента времени.
  Event struct {
    // Data в этом примере простой int, но в действительной
    // реализации это будет зависеть от приложения.
    Data int64
  }

  // Observer определяет стандартный интерфейс 
  // для экземпляров, которые хотят
  // быть в списке оповещения о возникновении 
  // определенного события.
  Observer interface {
    // OnNotify позволяет событию 
    // быть "опубликованным" для реализаций интерфейса.
    // В "реальном мире" здесь скорее всего 
    // будет реализована обработка ошибок.
    OnNotify(Event)
  }

  // Notifier - это наблюдаемый экземпляр.
  Notifier interface {
    // Register позволяет экземпляру зарегистрироваться 
    // для прослушивания/наблюдения за событиями.
    Register(Observer)
    // Deregister позволяет экземпляру 
    // удалить себя из коллекции
    // наблюдателей/слушателей.
    Deregister(Observer)
    // Notify публикует новые события для слушателей.
    // Метод не обязателен, так как 
    // каждая реализация может определить это сама
    // без потери функциональности.
    Notify(Event)
  }
)

Использование

Ниже приведен пример использования паттерна Observer и ссылка песочницу на play.golang.org

// Package main слуджит примером приложения, которое использует паттерн observer.
// Песочница: https://play.golang.org/p/GWwjZENw978
package main

import (
 "fmt"
 "time"
)

type (
 // Event определяет индикацию возникновения момента времени.
 Event struct {
  // Data в этом примере простой int, но в действительной
  // реализации это будет зависеть от приложения.
  Data int64
 }

 // Observer определяет стандартный интерфейс для экземпляров, которые хотят
 // быть в списке оповещения о возникновении определенного события.
 Observer interface {
  // OnNotify позволяет событию быть "опубликованным" для реализаций интерфейса.
  // В "реальном мире" здесь скорее всего будет реализована обработка ошибок.
  OnNotify(Event)
 }

 //  Notifier - это наблюдаемый экземпляр.
 Notifier interface {
  // Register позволяет экземпляру зарегистрироваться 
  // для прослушивания/наблюдения за событиями.
  Register(Observer)
  // Deregister позволяет экземпляру удалить себя из коллекции
  // наблюдателей/слушателей.
  Deregister(Observer)
  // Notify публикует новые события для слушателей.
  // Метод не обязателен, так как каждая реализация может определить это сама
  // без потери функциональности.
  Notify(Event)
 }
)

type (
 eventObserver struct{
  id int
 }

 eventNotifier struct{
  // Использование map с пустым struct позволяет хранить наблюдателей
  // уникальными, использая при этом мало памяти.
  observers map[Observer]struct{}
 }
)

func (o *eventObserver) OnNotify(e Event) {
 fmt.Printf("*** Observer %d получен: %d\n", o.id, e.Data)
}

func (o *eventNotifier) Register(l Observer) {
 o.observers[l] = struct{}{}
}

func (o *eventNotifier) Deregister(l Observer) {
 delete(o.observers, l)
}

func (p *eventNotifier) Notify(e Event) {
 for o := range p.observers {
  o.OnNotify(e)
 }
}

func main() {
 // Инициализируем новый Notifier.
 n := eventNotifier{
  observers: map[Observer]struct{}{},
 }

 // Регистрируем пару наблюдателей.
 n.Register(&eventObserver{id: 1})
 n.Register(&eventObserver{id: 2})

 // Простой цикл, публикующий текущий Unix timestamp для наблюдателей.
 stop := time.NewTimer(10 * time.Second).C
 tick := time.NewTicker(time.Second).C
 for {
  select {
  case <- stop:
   return
  case t := <-tick:
   n.Notify(Event{Data: t.UnixNano()})
  }
 }
}

Вывод:

*** Observer 1 получен: 1257894001000000000
*** Observer 2 получен: 1257894001000000000
*** Observer 1 получен: 1257894002000000000
*** Observer 2 получен: 1257894002000000000
*** Observer 1 получен: 1257894003000000000
*** Observer 2 получен: 1257894003000000000
*** Observer 1 получен: 1257894004000000000
*** Observer 2 получен: 1257894004000000000
*** Observer 1 получен: 1257894005000000000
*** Observer 2 получен: 1257894005000000000
*** Observer 1 получен: 1257894006000000000
*** Observer 2 получен: 1257894006000000000
*** Observer 1 получен: 1257894007000000000
*** Observer 2 получен: 1257894007000000000
*** Observer 1 получен: 1257894008000000000
*** Observer 2 получен: 1257894008000000000
*** Observer 1 получен: 1257894009000000000
*** Observer 2 получен: 1257894009000000000
*** Observer 1 получен: 1257894010000000000
*** Observer 2 получен: 1257894010000000000

В песочнице play.golang.org


Читайте также:


Комментариев нет:

Отправить комментарий