Minstrel

Ruby, JavaScript, Haskell, Math, Music, Design

Ruby2.5.1のObservableモジュールを写経した

場所

~/.rbenv/versions/2.5.1/lib/ruby/2.5.0/observer.rb

大きな流れ

①Subject(変更する側)からadd_observerを呼び出してConcreateObserver(変更通知を受け取る側)のObjectとインターフェイス(デフォルトは:update)を登録する ②changed(true)を呼び出し変更フラグを立てる。これがtrueでなければnotify_observersは動かない(return falseされる) ③notify_observersを呼び出し、Observerの:updateメソッドが呼ばれる

呼び出し側のコード

require 'observer'


class Employee
  include Observable

  attr_reader :name, :title, :salary

  def initialize(name, title, salary)
    @name   = name
    @title  = title
    @salary = salary
    #①add_observerメソッド →  Employeeの変更を通知したいObserverObjectを注入する
    add_observer(Payroll.new) 
    add_observer(TaxMan.new)
  end

  def salary=(new_salary)
    @salary = new_salary
    #changed(state=true)を書くと、notify_observersが呼ばれる。
    # つまり明示的に通知するかどうかを示せる
    changed(true) # ②observer_stateをtrueに変更する(これがfalseだとnotify_observerを呼んでも通知されない)
    notify_observers(self) # ③登録したObserverObjectに通知をする
  end
end

class Payroll
  def update(changed_employee)
    puts "給料あがった#{changed_employee.salary}"
  end
end

class TaxMan
  def update(changed_employee)
    puts "#{changed_employee.name} 請求書"
  end
end

john = Employee.new('John', 'Senior Vice President', 5000)
john.salary = 6000

写経したObservableのコード

module 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[observer] = func
    #①{ ObserverObject, :update } を配列にセットする
  end

  #.... 省略(delete_observer, count_observersなど補助的メソッド)

  def changed(state=true) #②変更がなされたかどうかのステータス
    @observer_state = state
  end

  def changed?
    if defined? @observer_state and @observer_state
      true
    else
      false
    end
  end

  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
end

感想

実装がとてもシンプル。初中級社のソースコードリーディングの練習に良いかも。 2.5.1のdefined便利。(nilガードとかではなく、変数やメソッドが定義されるかどうかを判別できる)