この記事は『Head Firstデザインパターン』の内容を自分なりにメモしたものです。 サンプルコードをrubyで書き換えたりしているので、玄人の方はぜひコメントを!! 僕のように初級者で気になった方はぜひ書籍の方を確認してみてください^^
気象情報の変更を受けて表示を変更する
構成はこんな感じ
あらかじめ気象情報を提供してくれるAPIがあって、そこにWeatherDataオブジェクトがアクセスして表示を更新する
最初のコード
class WeatherData def measurements_changed temp = get_temperature humidity = get_humidity pressure = get_presure current_conditions_display.update(temp, humidity, pressure) statistics_display.update(temp, humidity, pressure) forecasts_display.update(temp, humidity, pressure) end end
情報を取得して、更新するっていうシンプルなコードです。(これだけじゃもちろん動かないです)
問題の発生
問題点は、~displayという具体的なオブジェクトを直接ロジックに含んでしまっているため、他のdisplayを追加したい時にプログラムの変更をしないといけません。
相互にやり取りするオブジェクトは疎結合にする
構成
実際のコード(displayは拡張可能なようにDisplayElementを継承するようになってます)
module Subject def register_observer observer raise 'should implement register_observer method' end def remove_observer observer raise 'should implement remove_observer method' end def notify_observers raise 'should implement notify_observers method' end end class WeatherData include Subject attr_accessor :observers, :temperature, :humidity, :pressure def initialize @observers = Array.new end def register_observer observer @observers.push(observer) end def remove_observer observer end def notify_observers @observers.each do |observer| observer.update(@temperature, @humidity, @pressure) end end def mesurements_changed notify_observers end def set_mesurements temperature, humidity, pressure @temperature = temperature @humidity = humidity @pressure = pressure mesurements_changed end end module Observer def update temp, humidity, pressure raise 'should implement update method' end end class DisplayElement include Observer def display raise 'should implement display method' end end class CurrentConditionsDisplay < DisplayElement attr_accessor :temperature, :humidity, :weather_data def initialize weather_data @weather_data = weather_data weather_data.register_observer(self) end def update temperature, humidity, pressure @temperature = temperature @humidity = humidity display() end def display puts "Current Weather info: temperature #{@temperature} humidity #{@humidity}" end end
実行コードは
weather_data = WeatherData.new current_display1 = CurrentConditionsDisplay.new(weather_data) current_display2 = CurrentConditionsDisplay.new(weather_data) weather_data.set_mesurements(27, 65, 30.4)
weather_dataのset_mesurementsが呼び出されるとdisplayが更新されるようになってます。
observerを読み込んでいる+コンストラクタでwheather_dataを取り込んでいればweather_dataの情報変更を知ることができるようになります。
このままだと変更をすべて送信してしまうので、subjectのデータを送るかどうかのboolを持つケースもあったりします。(というかそっちの方が多いです)
ボタンをおした時のイベントを取得したりとか、かなり身近なところで使われているパターンということです。
Observerパターンの特徴
Subjectを実装したオブジェクトの状態変化をオブザーバに通知できる。
オブザーバの追加や削除も可能。
参考文献

Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本
- 作者: Eric Freeman,Elisabeth Freeman,Kathy Sierra,Bert Bates,佐藤直生,木下哲也,有限会社福龍興業
- 出版社/メーカー: オライリージャパン
- 発売日: 2005/12/02
- メディア: 大型本
- 購入: 14人 クリック: 362回
- この商品を含むブログ (98件) を見る