模块 Observable

观察者模式(也称为发布/订阅)提供了一种简单的机制,让一个对象可以在其状态发生变化时通知一组感兴趣的第三方对象。

机制

通知类混合了 Observable 模块,该模块提供了管理关联观察者对象的 方法。

可观察对象必须

观察者使用 Observable#add_observer 订阅更新,这也会指定通过 notify_observers 调用的方法。 notify_observers 的默认方法是 update。

示例

以下示例很好地演示了这一点。一个 Ticker 在运行时会不断接收其 @symbol 的股票 Price。一个 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

与 proc 的使用

#notify_observers 方法也可以通过使用 :call 作为 func 参数与 +proc+s 一起使用。

以下示例说明了 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

常量

版本

公共实例方法

add_observer(observer, func=:update) 点击切换源代码

observer 添加为该对象的观察者。这样它就会收到通知。

观察者

将收到更改通知的对象。

函数

Symbol,命名将在该 Observable 发生更改时调用的方法。

此方法必须对 observer.respond_to? 返回 true,并且将在调用 notify_observers 时接收 *arg,其中 *arg 是由该 Observable 传递给 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
changed(state=true) 点击切换源代码

设置该对象的更改状态。只有当更改的 statetrue 时才会发送通知。

状态

布尔值,指示该 Observable 的更改状态。

# File lib/observer.rb, line 194
def changed(state=true)
  @observer_state = state
end
changed?() 点击切换源代码

如果该对象的状态自上次调用 notify_observers 以来已更改,则返回 true。

# File lib/observer.rb, line 202
def changed?
  if defined? @observer_state and @observer_state
    true
  else
    false
  end
end
count_observers() 点击切换源代码

返回与该对象关联的观察者数量。

# File lib/observer.rb, line 180
def count_observers
  if defined? @observer_peers
    @observer_peers.size
  else
    0
  end
end
delete_observer(observer) 点击切换源代码

从该对象中删除 observer 作为观察者,使其不再接收通知。

观察者

Observable 的观察者

# File lib/observer.rb, line 166
def delete_observer(observer)
  @observer_peers.delete observer if defined? @observer_peers
end
delete_observers() 点击切换源代码

删除与该对象关联的所有观察者。

# File lib/observer.rb, line 173
def delete_observers
  @observer_peers.clear if defined? @observer_peers
end
notify_observers(*arg) 点击切换源代码

如果该对象的更改状态为 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