Rubyデザインパターン 3日目 : Observer

Rubyデザインパターン学習のために、自分なりに読書の結果をまとめていくことに決めました。第3日目はObserverです。(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=morizyun00-22)

![スクリーンショット 2015-07-27 11.25.28.png](https://qiita-image-store.s3.amazonaws.com/0/80075/d68a4010-c177-4ef5-2f2e-899a76c76cb6.png)


# **<i class="fa fa-pencil"></i>　3日目 Observer**

今日のパターンは、Observerパターンです。
このパターンは**変化に追従し、観察する**ことです。

例を挙げると、スプレッドシートのようなソフトウェアの開発時、一つのパラメータを変更したときにそのパラメータを変更した事が他の部分に知れ渡ることが必要なケースが多い時に、このパターンは特に有効になってきます。

これから、より具体的な例で考えてみます。
従業員データを管理するコードがあり、その中のデータを一つでも変更すると、通知が送られるようなシステムです。

```rb
class Employee #単に従業員のデータを保持しているだけのクラス  
  attr_reader :name
  attr_accessor :title, :salary

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

momozono = Employee.new("Momozono Tarou", "Normal", 200000) #データを入力

momozono.salary = 220000 #昇給
```
上記のコードは、ただ単に従業員データを保持するコードです。
平社員で20万円の給料をもらっているMomozono Tarouさんがいるということですね。
そして、salaryパラメータにアクセサを付けて代入することにより、給料を22万に昇級をさせています。

このコードだけでは出力の手段を持たないので、アウトプットの機能を付けてみましょう。

```rb
class Payroll
  def update(changed_employee)
    puts "#{changed_employee.name}さんについての情報です。"
    puts "彼の給料は現在#{changed_employee.salary}です"
  end
end

class Employee
  attr_reader :name, :title
  attr_reader :salary 

  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

payroll = Payroll.new

momozono = Employee.new("Momozono Tarou", "Normal", 220000, payroll)
momozono.salary = 210000
```
Employeeオブジェクトに出力用のPayrollオブジェクトを渡しています。
これにより`@payroll`にPayrollオブジェクトが入ります。salaryメソッドが実行されたときに`@salary`の値が更新され、それと同時にupdateメソッドが呼び出され、給与が更新されたことを通知するという設計です。
`attr_accessor :salary`を用いずに`attr_reader :salary`と`saraly=`メソッドを用いているのは、Payrollオブジェクトに値を渡すためです。

これで、最低限の機能である、給与の更新とその通知機能が実装されました。

さて、この状態からもう一つ機能を増やして、「給料が更新されたときに、税金の請求書を送る機能」を追加してみましょう。(細かいところはつっこまないでください汗)

## <i class="fa fa-eye"></i>  Observerパターンを構築してみる
```rb
class Payroll 
  def update(changed_employee)
    puts "#{changed_employee.name}さんの情報です"
    puts "彼の給料は今#{changed_employee.salary}円です"
  end
end

class Taxman
  def update(changed_employee)
    puts "#{changed_employee.name}に新しい税金の請求書を送ります!"
  end
end


class Employee 
  attr_reader :name, :title
  attr_reader :salary 

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

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

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

  def add_observer(observer)
    @observers << observer
  end
end

momozono = Employee.new('Momozono', 'Normal',220000)

payroll = Payroll.new
taxman  = Taxman.new

momozono.add_observer(payroll) 
momozono.add_observer(taxman)

momozono.salary = 230000
```


`@observers`というパラメータを作ることにより、そこにオブザーバを配列として格納します。配列の形で機能を持つ事により、機能の拡張がとても簡単になります。
`salary`メソッドから`notify_observers`メソッドを呼び出すことによって通知を制御しています。

**機能を配列の形で持つ**というニュアンスが既にステキですよね。可能性を感じます。

## <i class="fa fa-star-half"></i> 機能を分割する！
さて、このままでも従業員データのセットとObserverの機能は保たれていますが、**従業員のデータセットのためのEmployeeクラスの中に、Observerを提供する機能が盛り込まれており**若干ごたごたしております。
一概に言えないのかもしれませんが、違う種類の機能を複数持つクラスは分離したほうがいいでしょう <i class="fa fa-lightbulb-o"></i>

```rb
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

class Employee
  include Subject

  attr_reader :name, :address
  attr_reader :salary

  def initialize(name, title, salary)
    super() #moduleのinitialize呼び出し
    @name = name
    @title = title
    @salary = salary
  end

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

こうすることで、Observer提供機能は全てモジュールに分離することに成功しました！
**classではなくmoduleにすることにより、継承ベースの連携を避けています。**

## **<i class="fa fa-diamond"></i> Rubyらしい構成に...**
```rb
module Subject
  def initialize
    @observers = []
  end

  def add_observer(&observer) #ここでobserverにコードブロックが入る
    @observers << observer
  end

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

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

class Employee
  include Subject

  attr_accessor :name, :title, :salary

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

  def title=(new_title)
    old_title = @title
    if old_title != new_title
      @title = new_title
    end
  end

  def salary=(new_salary)
    old_salary = @salary
    if old_salary != new_salary
      @salary = new_salary
    end
  end

  def changes_complete
    notify_observers
  end
end

CHANGED_EMPLOYEE = lambda do |changed_employee|
  puts "従業員の名前が変更されました: #{changed_employee.name}さん"
  puts "給料: #{changed_employee.salary}"
  puts "職業: #{changed_employee.title}になりました"
end

momozono = Employee.new("Momozono", "Normal", 220000)

momozono.add_observer(&CHANGED_EMPLOYEE) 

momozono.salary = 0
momozono.title = "無職"

momozono.changes_complete
```

上記のコードでは、給与や等級が変更されたときのみ、通知が飛ぶようになっています。
lambdaを使う事により、Observerとしての機能を完全にパーツ化できています。

### 実行の流れ
1. `add_observer`メソッドによりオブザーバを設定します。
2. 更新したい項目に値をセットします
3. `changes_complete`メソッドによりオブザーバに連絡を送ります
4. 無事に`CHANGED_EMPLOYEE`オブザーバが`call`メソッドにより実行されます

## **<i class="fa fa-star"></i>　まとめ**

Observerパターンは、中心クラスの変更を監視する必要があるときに用いられます。
中心クラスの役目はデータを変更し、それをオブザーバに教えるだけです。
またコードブロックも引数にとる形にすれば、より柔軟性は高まりますね。