module Observable
觀察者模式(也稱為發佈/訂閱)提供了一個簡單的機制,讓一個物件在狀態改變時通知一組有興趣的第三方物件。
機制¶ ↑
發出通知的類別會混入 Observable
模組,此模組提供管理關聯觀察者物件的方法。
可觀察物件必須
-
宣告它具有
#changed
-
呼叫
#notify_observers
觀察者使用 Observable#add_observer
訂閱更新,此方法也會指定透過 notify_observers
呼叫的方法。notify_observers
的預設方法是 update。
範例¶ ↑
以下範例清楚地示範了這一點。當 Ticker
執行時,會持續接收其 @symbol
的股票 Price
。Warner
是價格的常駐觀察者,並示範了兩個 warner,分別是 WarnLow
和 WarnHigh
,如果價格低於或高於其設定的限制,它們會印出警告。
update
回呼函數允許 warner 在未被明確呼叫的情況下執行。系統會設定 Ticker
和多個觀察者,而觀察者會執行其職責,而頂層程式碼無需干預。
請注意,發布者和訂閱者(可觀察物件和觀察者)之間的合約並未宣告或強制執行。Ticker
發布時間和價格,而 warner 會接收這些資料。但如果您不確保合約正確,沒有其他東西可以警告您。
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
與程序搭配使用¶ ↑
#notify_observers
方法也可以與 +proc+ 一起使用,方法是將 :call
用作 func
參數。
以下範例說明了 lambda 的使用方式
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
公開實例方法
將 observer
新增為此物件上的觀察者。這樣它才能接收通知。
觀察者
-
將收到變更通知的物件。
函式
-
此方法必須傳回 true 以供
observer.respond_to?
使用,且當呼叫notify_observers
時將收到*arg
,其中*arg
是此可觀察
傳遞給notify_observers
的值
# 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
如果此物件的狀態自上次 notify_observers
呼叫後已變更,則傳回 true。
# File lib/observer.rb, line 202 def changed? if defined? @observer_state and @observer_state true else false end end
傳回與此物件關聯的觀察者數量。
# 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
移除與此物件關聯的所有觀察者。
# File lib/observer.rb, line 173 def delete_observers @observer_peers.clear if defined? @observer_peers end
如果 此物件的已變更狀態為 true
,則通知觀察者狀態已變更。
這會呼叫 add_observer
中指定的函式,傳遞 *arg
。然後,已變更狀態會設定為 false
。
*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