Глянь мой новый курс по Git! Привет! Глянь мой новый курс по Git! Привет! Глянь мой новый курс по Git на GitByBit.com! Привет! Хочешь круто подтянуть Git? Глянь мой новый курс на GitByBit.com!
Наблюдатель

Наблюдатель на Ruby

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

При этом наблюдатели могут свободно подписываться и отписываться от этих оповещений.

Сложность:

Популярность:

Применимость: Наблюдатель можно часто встретить в Ruby коде, особенно там, где применяется событийная модель отношений между компонентами. Наблюдатель позволяет отдельным компонентам реагировать на события, происходящие в других компонентах.

Признаки применения паттерна: Наблюдатель можно определить по механизму подписки и методам оповещения, которые вызывают компоненты программы.

Концептуальный пример

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

main.rb: Пример структуры паттерна

# Интерфейс издателя объявляет набор методов для управлениями подписчиками.
#
# @abstract
class Subject
  # Присоединяет наблюдателя к издателю.
  #
  # @abstract
  #
  # @param [Observer] observer
  def attach(observer)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end

  # Отсоединяет наблюдателя от издателя.
  #
  # @abstract
  #
  # @param [Observer] observer
  def detach(observer)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end

  # Уведомляет всех наблюдателей о событии.
  #
  # @abstract
  def notify
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

# Издатель владеет некоторым важным состоянием и оповещает наблюдателей о его
# изменениях.
class ConcreteSubject < Subject
  # Для удобства в этой переменной хранится состояние Издателя, необходимое всем
  # подписчикам.
  attr_accessor :state

  # @!attribute observers
  # @return [Array<Observer>] attr_accessor :observers private :observers

  def initialize
    @observers = []
  end

  # Список подписчиков. В реальной жизни список подписчиков может храниться в
  # более подробном виде (классифицируется по типу события и т.д.)

  # @param [Observer] observer
  def attach(observer)
    puts 'Subject: Attached an observer.'
    @observers << observer
  end

  # @param [Observer] observer
  def detach(observer)
    @observers.delete(observer)
  end

  # Методы управления подпиской.

  # Запуск обновления в каждом подписчике.
  def notify
    puts 'Subject: Notifying observers...'
    @observers.each { |observer| observer.update(self) }
  end

  # Обычно логика подписки – только часть того, что делает Издатель. Издатели
  # часто содержат некоторую важную бизнес-логику, которая запускает метод
  # уведомления всякий раз, когда должно произойти что-то важное (или после
  # этого).
  def some_business_logic
    puts "\nSubject: I'm doing something important."
    @state = rand(0..10)

    puts "Subject: My state has just changed to: #{@state}"
    notify
  end
end

# Интерфейс Наблюдателя объявляет метод уведомления, который издатели используют
# для оповещения своих подписчиков.
#
# @abstract
class Observer
  # Получить обновление от субъекта.
  #
  # @abstract
  #
  # @param [Subject] subject
  def update(_subject)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

# Конкретные Наблюдатели реагируют на обновления, выпущенные Издателем, к
# которому они прикреплены.

class ConcreteObserverA < Observer
  # @param [Subject] subject
  def update(subject)
    puts 'ConcreteObserverA: Reacted to the event' if subject.state < 3
  end
end

class ConcreteObserverB < Observer
  # @param [Subject] subject
  def update(subject)
    return unless subject.state.zero? || subject.state >= 2

    puts 'ConcreteObserverB: Reacted to the event'
  end
end

# Клиентский код.

subject = ConcreteSubject.new

observer_a = ConcreteObserverA.new
subject.attach(observer_a)

observer_b = ConcreteObserverB.new
subject.attach(observer_b)

subject.some_business_logic
subject.some_business_logic

subject.detach(observer_a)

subject.some_business_logic

output.txt: Результат выполнения

Subject: Attached an observer.
Subject: Attached an observer.

Subject: I'm doing something important.
Subject: My state has just changed to: 1
Subject: Notifying observers...
ConcreteObserverA: Reacted to the event

Subject: I'm doing something important.
Subject: My state has just changed to: 10
Subject: Notifying observers...
ConcreteObserverB: Reacted to the event

Subject: I'm doing something important.
Subject: My state has just changed to: 2
Subject: Notifying observers...
ConcreteObserverB: Reacted to the event

Наблюдатель на других языках программирования

Наблюдатель на C# Наблюдатель на C++ Наблюдатель на Go Наблюдатель на Java Наблюдатель на PHP Наблюдатель на Python Наблюдатель на Rust Наблюдатель на Swift Наблюдатель на TypeScript