модуль 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
Открытые методы экземпляра
# 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.
# File lib/observer.rb, line 194 def changed(state=true) @observer_state = state end
Set изменённое состояние этого объекта. Уведомления будут отправляться только если изменённое state равно true.
-
state -
Булево значение, указывающее изменённое состояние этого
Observable.
# File lib/observer.rb, line 202
def changed?
if defined? @observer_state and @observer_state
true
else
false
end
end Возвращает true, если состояние этого объекта изменилось с момента последнего вызова notify_observers.
# File lib/observer.rb, line 180
def count_observers
if defined? @observer_peers
@observer_peers.size
else
0
end
end Возвращает количество наблюдателей, связанных с этим объектом.
# File lib/observer.rb, line 166 def delete_observer(observer) @observer_peers.delete observer if defined? @observer_peers end
Удалить observer как наблюдателя этого объекта, чтобы он больше не получал уведомлений.
-
observer -
Наблюдатель этого
Observable
# File lib/observer.rb, line 173 def delete_observers @observer_peers.clear if defined? @observer_peers end
Удаляет всех наблюдателей, связанных с этим объектом.
# 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.