Spec-Zone.ru › Ruby 3.1

модуль Observable

Шаблон наблюдателя (также известный как публикация/подписка) предоставляет простой механизм, позволяющий одному объекту уведомлять набор заинтересованных сторонних объектов об изменениях своего состояния.

Механизм

Уведомляющий класс использует модуль Observable, который предоставляет методы для управления связанными наблюдателями.

Объект наблюдаемого класса должен:

  • убедиться, что у него есть #changed

  • вызвать #notify_observers

Наблюдатель подписывается на обновления с помощью Observable#add_observer, который также указывает метод, вызываемый через notify_observers. По умолчанию метод для notify_observers — это update.

Пример

Следующий пример наглядно демонстрирует это. Программа Ticker, при запуске, постоянно получает данные о Price акций для своих @symbol. Warner — это общий наблюдатель цены, и демонстрируются два обработчика предупреждений: WarnLow и WarnHigh, которые выдают предупреждение, если цена ниже или выше их установленных пределов соответственно.

Обратный вызов update позволяет обработчикам предупреждений работать без явного вызова. Система настраивается с Ticker и несколькими наблюдателями, и наблюдатели выполняют свою работу без вмешательства кода верхнего уровня.

Обратите внимание, что контракт между издателем и подписчиком (наблюдаемым и наблюдателем) не объявлен и не проверяется. Ticker публикует время и цену, а обработчики предупреждений их получают. Но если вы не обеспечите корректность своих контрактов, ничего другого не предупредит вас.

require "observer"

class Ticker          ### Periodically fetch a stock price.
  include Observable

  def initialize(symbol)
    @symbol = symbol
  end

  def run
    last_price = nil
    loop do
      price = Price.fetch(@symbol)
      print "Current price: #{price}\n"
      if price != last_price
        changed                 # notify observers
        last_price = price
        notify_observers(Time.now, price)
      end
      sleep 1
    end
  end
end

class Price           ### A mock class to fetch a stock price (60 - 140).
  def self.fetch(symbol)
    60 + rand(80)
  end
end

class Warner          ### An abstract observer of Ticker objects.
  def initialize(ticker, limit)
    @limit = limit
    ticker.add_observer(self)
  end
end

class WarnLow < Warner
  def update(time, price)       # callback for observer
    if price < @limit
      print "--- #{time.to_s}: Price below #@limit: #{price}\n"
    end
  end
end

class WarnHigh < Warner
  def update(time, price)       # callback for observer
    if price > @limit
      print "+++ #{time.to_s}: Price above #@limit: #{price}\n"
    end
  end
end

ticker = Ticker.new("MSFT")
WarnLow.new(ticker, 80)
WarnHigh.new(ticker, 120)
ticker.run

Результат:

Current price: 83
Current price: 75
--- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75
Current price: 90
Current price: 134
+++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134
Current price: 134
Current price: 112
Current price: 79
--- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79

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

Метод #notify_observers также может быть использован с +proc+s, используя :call в качестве параметра func.

Следующий пример демонстрирует использование лямбда-выражения:

require 'observer'

class Ticker
  include Observable

  def run
    # logic to retrieve the price (here 77.0)
    changed
    notify_observers(77.0)
  end
end

ticker = Ticker.new
warner = ->(price) { puts "New price received: #{price}" }
ticker.add_observer(warner, :call)
ticker.run

Константы

VERSION

Открытые методы экземпляра

add_observer(observer, func=:update) Показать исходный код
# File lib/observer.rb, line 153
def add_observer(observer, func=:update)
  @observer_peers = {} unless defined? @observer_peers
  unless observer.respond_to? func
    raise NoMethodError, "observer does not respond to `#{func}'"
  end
  @observer_peers[observer] = func
end

Добавить observer в качестве наблюдателя этого объекта. Для того, чтобы он получал уведомления.

observer

объект, который будет уведомлен об изменениях.

func

Symbol имя метода, который будет вызван, когда у этого Observable произойдут изменения.

Этот метод должен возвращать true для observer.respond_to? и получит *arg при вызове notify_observers, где *arg — значение, переданное в notify_observers этим Observable.

changed(state=true) Показать исходный код
# File lib/observer.rb, line 194
def changed(state=true)
  @observer_state = state
end

Set изменённое состояние этого объекта. Уведомления будут отправляться только если изменённое state равно true.

state

Булево значение, указывающее изменённое состояние этого Observable.

changed?() Показать исходный код
# File lib/observer.rb, line 202
def changed?
  if defined? @observer_state and @observer_state
    true
  else
    false
  end
end

Возвращает true, если состояние этого объекта изменилось с момента последнего вызова notify_observers.

count_observers() Показать исходный код
# File lib/observer.rb, line 180
def count_observers
  if defined? @observer_peers
    @observer_peers.size
  else
    0
  end
end

Возвращает количество наблюдателей, связанных с этим объектом.

delete_observer(observer) Показать исходный код
# File lib/observer.rb, line 166
def delete_observer(observer)
  @observer_peers.delete observer if defined? @observer_peers
end

Удалить observer как наблюдателя этого объекта, чтобы он больше не получал уведомлений.

observer

Наблюдатель этого Observable

delete_observers() Показать исходный код
# File lib/observer.rb, line 173
def delete_observers
  @observer_peers.clear if defined? @observer_peers
end

Удаляет всех наблюдателей, связанных с этим объектом.

notify_observers(*arg) Показать исходный код
# File lib/observer.rb, line 218
def notify_observers(*arg)
  if defined? @observer_state and @observer_state
    if defined? @observer_peers
      @observer_peers.each do |k, v|
        k.__send__(v, *arg)
      end
    end
    @observer_state = false
  end
end

Уведомляет наблюдателей об изменении состояния если изменённое состояние этого объекта равно true.

Это вызовет метод, указанный в add_observer, передавая *arg. Изменённое состояние затем устанавливается в false.

*arg

Любые аргументы для передачи наблюдателям.

Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API