Autumn SALE
Наблюдатель

Наблюдатель на 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 [Obserser] observer
  def attach(observer)
    puts 'Subject: Attached an observer.'
    @observers << observer
  end

  # @param [Obserser] 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