модуль 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
Методы публичного экземпляра
# 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 -
Symbolимя метода, который будет вызван, когда у этого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
Set состояние изменения этого объекта. Уведомления будут отправлены только если состояние изменения 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.