🍣

Rubyで学ぶオブザーバーパターン (Observer Pattern)

2025/01/14に公開

1. どんなもの?

オブザーバーパターンは、あるオブジェクトの状態が変化したとき、依存する他のオブジェクト(オブザーバー)に通知するデザインパターンです。
これにより、複数のオブジェクト間で一貫性のあるデータ更新が実現されます。

2. 通常の実装方法と比べてどこがすごいの?

通常の方法

あるオブジェクトの変更に応じて他のオブジェクトの状態を更新する場合、直接呼び出す必要があります。

例: 通常の方法

class Publisher
  def initialize
    @state = nil
  end

  def change_state(new_state, observer)
    @state = new_state
    observer.update(@state)
  end
end

class Observer
  def update(state)
    puts "State updated to: #{state}"
  end
end

publisher = Publisher.new
observer = Observer.new
publisher.change_state("New State", observer) # State updated to: New State
  • 課題:
    • オブジェクト間の依存関係が強くなる。
      • PublisherがObserverクラスの存在を知っている必要があり、その update メソッドも知っている必要がある
      • そのため、Observerクラスが update メソッドを持たない場合や update のメソッド名を変更した場合、Publisherクラスも修正が必要になる
    • 複数のオブジェクトに通知するのが煩雑になる。

オブザーバーパターンの場合

オブザーバーパターンを使うと、オブジェクト間の依存をゆるく保ちながら、複数のオブジェクトに通知を送ることができます。

例: オブザーバーパターンの場合

require 'observer'

class Publisher
  include Observable

  def change_state(new_state)
    changed
    notify_observers(new_state)
  end
end

class Observer
  def update(state)
    puts "State updated to: #{state}"
  end
end

publisher = Publisher.new
observer = Observer.new

publisher.add_observer(observer)
publisher.change_state("New State") # State updated to: New State
  • 利点:
    • オブジェクト間の結合度が低い。
    • 通知先を動的に追加・削除できる。

3. 技術や手法の”キモ”はどこにある?

RubyのObservableモジュール:

  • Observableモジュールを使用することで、オブザーバーパターンを簡単に実装可能です。
  • changedとnotify_observersメソッドで通知処理を行います。

オブザーバーの登録と削除:

  • add_observerで通知先を動的に追加。
  • delete_observerで通知先を動的に削除。

結合度の低い設計:

  • 通知対象のオブジェクトが特定のクラスに依存しないため、柔軟性が向上します。

4. 実装例

例: RailsのActiveSupport::Notifications

Railsではイベント駆動の仕組みとしてActiveSupport::Notificationsが使われています。

require 'active_support/notifications'

# 1. イベントを監視するObserverを登録
ActiveSupport::Notifications.subscribe("user.created") do |name, start, finish, id, payload|
  puts "[LOG] Event: #{name}, User ID: #{payload[:user_id]}, Time Taken: #{finish - start}s"
end

# 2. ユーザー作成のイベントを発行する
def create_user(user_id)
  ActiveSupport::Notifications.instrument("user.created", user_id: user_id) do
    # ダミーのユーザー作成処理(時間をかける)
    sleep(1)
    puts "User with ID #{user_id} has been created!"
  end
end

# 実行例
create_user(42)
# User with ID 42 has been created!
# [LOG] Event: user.created, User ID: 42, Time Taken: 1.0s

create_user(100)
# User with ID 100 has been created!
# [LOG] Event: user.created, User ID: 100, Time Taken: 1.0s
  • 実装ポイント
    • イベントを発行する側(ActiveSupport::Notifications.instrument)はオブザーバーを直接知らないため、通知先が変更されても影響を受けない。
    • オブザーバー(ActiveSupport::Notifications.subscribe)はイベントを発行する側に直接依存しないため、通知を受け取るロジックを動的に追加・削除できる。

5. 議論はあるか?

メリット

  • 複数のオブジェクト間で一貫性のある状態管理が可能。
  • オブジェクト間の結合度が低いため、変更に強い設計が実現できる。

デメリット

  • オブザーバーの数が多くなると、通知処理が複雑化し、パフォーマンスに影響を与える可能性がある。
  • 状態の変化が追いにくくなる場合がある。
    • イベントの発行が連鎖したり、複数のイベント発行者がいる場合、状態の変化を追いにくくなる。

議論

オブザーバーパターンは強力ですが、通知対象の増加によるパフォーマンス低下や、イベントの追跡が難しくなる課題があります。
適切な場面で利用することが重要です。

6. まとめ

オブザーバーパターンは、オブジェクト間の依存関係を最小化しつつ、状態の変化を複数のオブジェクトに通知できる強力なデザインパターンです。

このパターンを活用することで、柔軟で拡張性の高いシステム設計を実現できる一方で、複雑さを管理する工夫も必要です。

GitHubで編集を提案

Discussion