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