JavaエンジニアがRubyでデザインパターンを学ぶ - Observer Pattern

# 概要
「Rubyによるデザインパターン」を読んでデザインパターンを勉強中。
Javaをやっていた人間としての目線で情報を整理してみます。

####他のデザインパターン
[Template Method Pattern](http://qiita.com/ksugimori/items/34e766e9bff4cfa62591)
[Strategy Pattern](http://qiita.com/ksugimori/items/f8773909ded7585c5d61)

# Observer Pattern

- Subject(観測対象) のオブジェクトは Observer(観測者) オブジェクトのコレクションを保持
- Subject に対して変更が加えられた場合などに Observer に対して通知する

![png.png](https://qiita-image-store.s3.amazonaws.com/0/86330/57bb062b-4f19-22e1-dc9e-30a1de42cf44.png)


## Java での実装

### Subject
```java:Subject.java
public interface Subject {
  public void addObserver(Observer observer);
  public void notifyObservers();
  public String getStatus(); // Subject からの情報取得を簡単にするためこれもインターフェースに入れておきます
}
```

### Observer
```java:Observer.java
public interface Observer {
  public void update(Subject subject);
}
```

### ConcreteSubject
```java:ConcreteSubject.java
import java.util.Set;
import java.util.HashSet;

public class ConcreteSubject implements Subject {
  private Set<Observer> observers;

  public enum Status {
    NORMAL, ERROR
  }
  private Status status;

  public void setStatus(Status status) {
    this.status = status;
    notifyObservers(); // status フィールドを更新したら Observer に通知するようにしておきます
  }

  public String getStatus() {
    return status.toString();
  }

  public ConcreteSubject() {
    observers = new HashSet<Observer>();
    status = Status.NORMAL;
  }

  public void addObserver(Observer observer) {
    observers.add(observer);
  }

  // Observer に通知を行う
  // Observer 側で ConcreteSubject の情報を取得するために this を渡しています
  public void notifyObservers() {
    for (Observer observer : observers) {
      observer.update(this);
    }
  }
}
```

### ConcreteObserver
```java:ConcreteObserver.java
public class ConcreteObserver implements Observer {
    // 通知されたら Subject のステータスを表示
    public void update(Subject subject) {
      System.out.printf("<Subject value=%s>%n", subject.getStatus());
    }
}
```


### 呼び出し
```java:Main.java
import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    ConcreteSubject subject = new ConcreteSubject();

    // Observer を指定
    subject.addObserver(new ConcreteObserver());

    // Subject を変更を加える度にすぐさま Observer に変更が通知される
    subject.setStatus(ConcreteSubject.Status.NORMAL);
    subject.setStatus(ConcreteSubject.Status.ERROR);
  }
}
```

### 実行結果

```
$> java Main
<Subject value=NORMAL>
<Subject value=ERROR>
```


## Ruby での実装

基本的に Java と同じように書けます。

### Subject

- Observer のコレクションを内部に持つ
- 全ての Observer に対して通知を行う（Observer#update メソッドを呼び出す）

という処理をモジュールとして実装し、それを include する形にします。
※Ruby の標準添付ライブラリ（observer）に [Observable](http://docs.ruby-lang.org/ja/2.3.0/class/Observable.html) というモジュールが存在するので、実際にはそれを使うことが多いと思います


```rb
module Subject
  def initialize
    @observers = []
  end

  def add_observer(observer)
    @observers << observer
  end

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

### Observer
不要。
各具象クラスで update メソッドを実装していれば良い。

### ConcreteSubject
```rb
class ConcreteSubject
  include Subject
  
  attr_accessor :status

  module Status
    NORMAL = "NORMAL"
    ERROR = "ERROR"
  end

  def status=(status)
    @status = status
    notify_observers
  end
end
```

### ConcreteObserver
```rb
class ConcreteObserver
  def update(subject)
    puts "<Subject status=#{subject.status}>"
  end
end
```


### 呼び出し
```rb
subject = ConcreteSubject.new
subject.add_observer(ConcreteObserver.new)

# status を更新すると ConcreteObserver に通知されます
subject.status = ConcreteSubject::Status::NORMAL
subject.status = ConcreteSubject::Status::ERROR
```

## Ruby での実装 （よりRubyらしく）
[Strategy Pattern](http://qiita.com/ksugimori/items/f8773909ded7585c5d61) でもそうでしたが、Observer 側の処理がシンプルならばわざわざクラスを定義せず、ブロックとして渡してしまう手もあります。

### Subject

```rb
module Subject
  def initialize
    @observers = []
  end

  def add_observer(&observer)  # observer は Proc オブジェクトとして保持
    @observers << observer
  end

  def notify_observers
    @observers.each do |obs|
      obs.call(self)           # Proc#call で処理呼び出し
    end
  end
end
```

### 呼び出し
```rb
subject = ConcreteSubject.new

# ブロックとして observer を登録
# 実行時は obs.call(self) として渡した self が changed_subject に入ります
subject.add_observer do |changed_subject|
  puts "<Subject status=#{changed_subject.status}>"
end

subject.status = ConcreteSubject::Status::NORMAL
subject.status = ConcreteSubject::Status::ERROR
```


# Sample Code

[Curses](https://github.com/ruby/curses) という、TUI（テキストユーザインターフェース）のアプリを作ることができるライブラリを使って、簡単な表計算を行うプログラムを書いてみました。
思ったよりコード量が多くなってしまったので全量は [こちら](https://gist.github.com/ksugimori/f630d2c1caeaa0376146572cae92930b) に置いています。

```rb
module ObservableHash
  include Observable

  def []=(k, v)
    super(k, v)
    changed
    notify_observers
  end
end

prices = {
  pencil: 30,
  book: 1500,
  stapler: 980
}
prices.extend(ObservableHash)
```

というように、Hash が更新されたら Observer に通知されるようにして、画面表示を更新する仕組みになっています。

![step1.png](https://qiita-image-store.s3.amazonaws.com/0/86330/1ec61de2-155d-f2df-7a65-599ac176d288.png)
![step2.png](https://qiita-image-store.s3.amazonaws.com/0/86330/b5fb7a4c-a1c6-d7d7-0fe3-49702c3fac0b.png)


# 参考
Olsen, R. 2007. [*Design Patterns in Ruby*](https://www.amazon.co.jp/dp/B004YW6M6G/ref=cm_sw_r_tw_dp_JODHxb1S34F51)

Curses についてはこちらを参考にさせてもらいました。
[Curses for Ruby｜やまいも｜note](https://note.mu/yappy0625/n/n1618a75951b7)