Spec-Zone.ru › Ruby 3

модуль 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–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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