diff options
Diffstat (limited to 'lib/observer.rb')
| -rw-r--r-- | lib/observer.rb | 108 |
1 files changed, 60 insertions, 48 deletions
diff --git a/lib/observer.rb b/lib/observer.rb index 64c7d81351..acfe654301 100644 --- a/lib/observer.rb +++ b/lib/observer.rb @@ -1,33 +1,36 @@ +# frozen_string_literal: true # -# observer.rb implements the _Observer_ object-oriented design pattern. The +# Implementation of the _Observer_ object-oriented design pattern. The # following documentation is copied, with modifications, from "Programming -# Ruby", by Hunt and Thomas; http://www.rubycentral.com/book/lib_patterns.html. +# Ruby", by Hunt and Thomas; http://www.ruby-doc.org/docs/ProgrammingRuby/html/lib_patterns.html. # -# == About -# -# The Observer pattern, also known as Publish/Subscribe, provides a simple +# See Observable for more info. + +# 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. +# when its state changes. # # == Mechanism # -# In the Ruby implementation, the notifying class mixes in the +Observable+ +# 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+ +# * assert that it has +#changed+ +# * call +#notify_observers+ # -# == Example +# An observer subscribes to updates using Observable#add_observer, which also +# specifies the method called via #notify_observers. The default method for +# #notify_observers is #update. +# +# === Example # # 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. +# continually receives the stock +Price+ for its <tt>@symbol</tt>. 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 @@ -39,22 +42,22 @@ # 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 +# last_price = nil # loop do # price = Price.fetch(@symbol) # print "Current price: #{price}\n" -# if price != lastPrice +# if price != last_price # changed # notify observers -# lastPrice = price +# last_price = price # notify_observers(Time.now, price) # end # sleep 1 @@ -63,18 +66,18 @@ # end # # class Price ### A mock class to fetch a stock price (60 - 140). -# def Price.fetch(symbol) +# 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 @@ -82,7 +85,7 @@ # end # end # end -# +# # class WarnHigh < Warner # def update(time, price) # callback for observer # if price > @limit @@ -108,36 +111,39 @@ # Current price: 112 # Current price: 79 # --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79 - - -# -# Implements the Observable design pattern as a mixin so that other objects can -# be notified of changes in state. See observer.rb for details and an example. -# module Observable # - # Add +observer+ as an observer on this object. +observer+ will now receive + # Add +observer+ as an observer on this object. So that it will receive # notifications. # - def add_observer(observer) - @observer_peers = [] unless defined? @observer_peers - unless observer.respond_to? :update - raise NoMethodError, "observer needs to respond to `update'" + # +observer+:: the object that will be notified of changes. + # +func+:: Symbol naming the method that will be called when this Observable + # has changes. + # + # This method must return true for +observer.respond_to?+ and will + # receive <tt>*arg</tt> when #notify_observers is called, where + # <tt>*arg</tt> is the value passed to #notify_observers by this + # Observable + 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.push observer + @observer_peers[observer] = func end # - # Delete +observer+ as an observer on this object. It will no longer receive - # notifications. + # Remove +observer+ as an observer on this object so that it will no longer + # receive notifications. # + # +observer+:: An observer of this Observable def delete_observer(observer) @observer_peers.delete observer if defined? @observer_peers end # - # Delete all observers associated with this object. + # Remove all observers associated with this object. # def delete_observers @observer_peers.clear if defined? @observer_peers @@ -158,12 +164,15 @@ module Observable # Set the changed state of this object. Notifications will be sent only if # the changed +state+ is +true+. # + # +state+:: Boolean indicating the changed state of this Observable. + # def changed(state=true) @observer_state = state end # - # Query the changed state of this object. + # Returns true if this object's state has been changed since the last + # #notify_observers call. # def changed? if defined? @observer_state and @observer_state @@ -174,16 +183,19 @@ module Observable end # - # If this object's changed state is +true+, invoke the update method in each - # currently associated observer in turn, passing it the given arguments. The - # changed state is then set to +false+. + # Notify observers of a change in state *if* this object's changed state is + # +true+. + # + # This will invoke the method named in #add_observer, passing <tt>*arg</tt>. + # The changed state is then set to +false+. # + # <tt>*arg</tt>:: Any arguments to pass to the observers. def notify_observers(*arg) if defined? @observer_state and @observer_state if defined? @observer_peers - for i in @observer_peers.dup - i.update(*arg) - end + @observer_peers.each do |k, v| + k.send v, *arg + end end @observer_state = false end |
