observer.rb implements the Observer object-oriented design pattern. The following documentation is copied, with modifications, from “Programming Ruby”, by Hunt and Thomas; www.rubycentral.com/book/lib_patterns.html.
The Observer pattern, also known as Publish/Subscribe, provides a simple mechanism for one object to inform a set of interested third-party objects when its state changes.
In the Ruby implementation, the notifying class mixes in the
Observable
module, which provides the methods for managing the
associated observer objects.
The observers must implement the update
method to receive
notifications.
The observable object must:
assert that it has changed
call notify_observers
The following example demonstrates this nicely. A Ticker
,
when run, continually receives the stock Price
for its
+@symbol+. A Warner
is a general observer of the price, and
two warners are demonstrated, a WarnLow
and a
WarnHigh
, which print a warning if the price is below or above
their set limits, respectively.
The update
callback allows the warners to run without being
explicitly called. The system is set up with the Ticker
and
several observers, and the observers do their duty without the top-level
code having to interfere.
Note that the contract between publisher and subscriber (observable and
observer) is not declared or enforced. The Ticker
publishes a
time and a price, and the warners receive that. But if you don't
ensure that your contracts are correct, nothing else can warn you.
require "observer" class Ticker ### Periodically fetch a stock price. include Observable def initialize(symbol) @symbol = symbol end def run lastPrice = nil loop do price = Price.fetch(@symbol) print "Current price: #{price}\n" if price != lastPrice changed # notify observers lastPrice = 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 Price.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
Produces:
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