Rubyによるデザインパターン【Observer】-本日のニュースをお届けします-


# 概要

[Rubyによるデザインパターン](http://www.amazon.co.jp/gp/product/4894712857/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4894712857&linkCode=as2&tag=kidachi-22)第5章。
Observer Pattern。

[Rubyによるデザインパターン5原則](http://qiita.com/kidachi_/items/4b63de9ad5a97726c50c)に則って理解する。


# どんなパターンか

あるオブジェクトの状態に関心のあるオブジェクトに、都度通知を送る。

ニュースの発信源（Subject）とニュースの消費者（Observer）間に綺麗なインターフェイスを用意する。


### Subject

あるニュースを配信するクラス

### Observer

あるニュースを得ることに関心があるクラス

# メリット

ニュースの発信者と受信者の間の依存関係を排除する。




# 問題のあるコード

従業員の給与の変化を経理部門に伝えるシステム


## 従業員クラス

```ruby
class Employee
  attr_reader :salary
  attr_accessor :title, :name

  def initialize( name, title, salary, payroll)
    @name = name
    @title = title
    @salary = salary
    @payroll = payroll
  end

  def salary=(new_salary)
    @salary = new_salary
    @payroll.update(self)
  end

end
```

## 経理部門クラス

```ruby
class Payroll
  def update(changed_employee)  # Subjectオブジェクトを受け取る
    puts "#{changed_employee.name}の給料が#{changed_employee.salary}ドルに上がりました!"
  end
end
```

## クラスの利用

```ruby
payroll = Payroll.new
john = Employee.new('john', 'worker', 100, payroll)
john.salary = 200
=> johnの給料が200ドルに上がりました!
```


# 問題

もし経理部門以外にも通知したくなったら？

→今はEmployeeクラスに手を入れる必要がある。
本質的にはEmployeeに対する変更など何もないにも関わらず・・。


そこで、

### 変化する事項（「従業員の給与の変更」というニュースを誰が受け取るか）を、Employeeオブジェクトから分離する。

→必要なのは、Employeeオブジェクトの変化に関心のあるオブジェクトの一覧。

## 従業員クラス

```ruby
class Employee
  attr_reader :salary
  attr_accessor :title, :name

  def initialize(name, title, salary)
    @name = name
    @title = title
    @salary = salary
    @observers = []
  end

  def add_observer(observer)
    @observers << observer
  end

  def salary=(new_salary)
    @salary = new_salary
    notify_observers
  end

  private
  def notify_observers
    @observers.each do |observer|
      observer.update(self)
    end
  end

end
```

## 経理部門クラス

```ruby
class Payroll
  def update(changed_employee)  # Subjectオブジェクトを受け取る
    puts "#{changed_employee.name}の給料が#{changed_employee.salary}ドルに上がりました!"
    puts "経理部門は#{changed_employee.name}に小切手を切ります！"
  end
end
```

## 税務署員クラス

```ruby
class Taxman
  def update(changed_employee)
    puts "#{changed_employee.name}の給料が#{changed_employee.salary}ドルに上がりました!"    
    puts "税務署員は#{changed_employee.name}に新しい税金請求書を送ります！"
  end
end
```

## クラスの利用

```ruby
[4] pry(main)> john = Employee.new('john', 'worker', 100)
[5] pry(main)> john.add_observer(Payroll.new)
[6] pry(main)> john.add_observer(Taxman.new)
[7] pry(main)> john.salary = 200
johnの給料が200ドルに上がりました!
経理部門はjohnに小切手を切ります！
johnの給料が200ドルに上がりました!
税務署員はjohnに新しい税金請求書を送ります！
```


なかなか良くなった。
しかし、他のクラスにもサブジェクトの機能を持たせたくなった時、都度同じコードを書く必要がある。

# モジュール化（1）

サブジェクトとしての機能をモジュール化し、ポータブルにする。

## Subjectモジュール

```ruby
module Subject

  def initialize
    @observers=[]
  end

  def add_observer(observer)
    @observers << observer
  end

  def delete_observer(observer)
    @observers.delete(observer)
  end

  def notify_observers
    @observers.each do |observer|
      observer.update(self)
    end
  end

end
```

## モジュール利用（クラスのSubject化）

```ruby
class Employee
  include Subject

  attr_reader :salary
  attr_accessor :name, :address

  def initialize(name, title, salary)
    super()  # Subject Moduleのinitializeを行う
    @name = name
    @title = title
    @salary = salary
  end

  def salary=(new_salary)
    old_salary = @salary
    @salary = new_salary
    if old_salary != new_salary  # oldとnewが異なっている場合のみ通知
      notify_observers
    end
  end

end

```

Employeeのinitializeメソッドの中でsuper()を呼び出すことに注意。
ここでSubjectモジュールのinitializeを呼び出している。

# モジュール化（2）


実はRubyには、observableというobserver用Moduleが存在する。

## モジュール利用（クラスのSubject化）

```ruby
require 'observer'

class Employee
  include Observable

  attr_reader :salary
  attr_accessor :name, :address

  def initialize(name, title, salary)
    @name = name
    @title = title
    @salary = salary
  end

  def salary=(new_salary)
    old_salary = @salary
    @salary = new_salary
    if old_salary != new_salary  # oldとnewが異なっている場合のみ通知
      changed  # changedメソッドの呼び出し
      notify_observers
    end
  end

end

```

### changedメソッドとは

Observableには、オブザーバへ冗長な通知を避けるために、
オブジェクトが本当に変更されたかどうかを示すBooleanフラグが存在する。
changedは、これをtrueに設定する。

notify_observersを呼び出すごとに、変更フラグはfalseへとリセットされる。


# ブロックによるオブザーバー

ブロックによるオブザーバーによって簡潔なコードを実現する。
また、Observable（モジュール（2））はブロックをサポートしていないため、
独自実装であるモジュール（1）を拡張して実現してみる。

## Subjectモジュール（ブロックをサポート）

```
module Subject

  def initialize
    @observers=[]
  end

  def add_observer(&observer)  # blockを受け取り、procへ変換(&)してobserverとして登録
    @observers << observer
  end

  def delete_observer(observer)
    @observers.delete(observer)
  end

  def notify_observers
    @observers.each do |observer|
      observer.call(self)  # observer(Proc)を呼び出す
    end
  end

end
```

## モジュール利用（クラスのSubject化）

```ruby
class Employee
  include Subject
  attr_accessor :salary, :name, :address

  def initialize(name, title, salary)
    super()
    @name = name
    @title = title
    @salary = salary
  end

  def salary=(new_salary)
    old_salary = @salary
    @salary = new_salary
    if old_salary != new_salary  # oldとnewが異なっている場合のみ通知
      notify_observers
    end
  end
end
```

## 呼び出し

```ruby
[13] pry(main)> john = Employee.new('john', 'worker', 100)
[14] pry(main)> john.add_observer do |changed_employee|  # observerブロックの登録
[14] pry(main)*   puts("#{changed_employee.name}に新しい小切手を切ります!")
[14] pry(main)* end
[15] pry(main)> john.salary = 200
johnに新しい小切手を切ります!

```


# まとめ

- 変わるもの（オブザーバー）と変わらないもの（サブジェクト）を分離して、変化に強い構造へ
- サブジェクトの機能自体はモジュール化してボータブル化
- ブロックによるオブザーバーの登録も可能


## Strategyパターンとの比較

### 形は似ている

Observerパターン：サブジェクトがオブザーバーを呼び出す
Strategyパターン：コンテキストがストラテジを呼び出す

### 違いは目的

Observerパターン：サブジェクトで発生したイベントをオブザーバーへ通知する
Strategyパターン：コンテキストが、何か特定の処理を行うためにストラテジを呼び出す

# 以下へ続く

【Composite】 -世界は再帰的（部分は全体、全体は部分）-
http://qiita.com/kidachi_/items/6cb73b2bbc875e9bef6d