модуль Observable
Шаблон наблюдателя (также известный как публикация/подписка) предоставляет простой механизм для одного объекта, чтобы сообщать набору заинтересованных сторонних объектов об изменениях в его состоянии.
Механизм
Уведомляющий класс смешивает модуль Observable, который предоставляет методы для управления связанными наблюдателями.
Объект, подлежащий наблюдению, должен:
-
убедиться, что у него есть
#changed -
вызвать
#notify_observers
Наблюдатель подписывается на обновления, используя #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
Публичные методы экземпляров
# File lib/observer.rb, line 127
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 -
Символ с именем метода, который будет вызван, когда у этого Observable произойдут изменения.
Этот метод должен возвращать true для
observer.respond_to?и получит*argпри вызове notify_observers, где*arg— это значение, переданное в notify_observers этим Observable
# File lib/observer.rb, line 168 def changed(state=true) @observer_state = state end
Установить состояние изменения этого объекта. Уведомления будут отправляться только в том случае, если состояние изменения state равно true.
-
state -
Булево значение, указывающее состояние изменения этого Observable.
# File lib/observer.rb, line 176
def changed?
if defined? @observer_state and @observer_state
true
else
false
end
end Возвращает true, если состояние этого объекта изменилось с момента последнего вызова notify_observers.
# File lib/observer.rb, line 154
def count_observers
if defined? @observer_peers
@observer_peers.size
else
0
end
end Возвращает количество наблюдателей, связанных с этим объектом.
# File lib/observer.rb, line 140 def delete_observer(observer) @observer_peers.delete observer if defined? @observer_peers end
Удалить observer как наблюдателя за этим объектом, чтобы он больше не получал уведомлений.
-
observer -
Наблюдатель этого Observable
# File lib/observer.rb, line 147 def delete_observers @observer_peers.clear if defined? @observer_peers end
Удаляет всех наблюдателей, связанных с этим объектом.
# File lib/observer.rb, line 192
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–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.