コーヒー飲みながら仕事したい

仕事で使う技術的なことの備忘録とか

Observer パターンを自分なりに整理してみる

免責

いきなり免責というのもアレですが、この記事の真偽は一切保証を致しかねます。
正直、かなり怪しいと思いますので、少なくとも情報収集されている方は、この記事はスキップされたほうが無難かと思われます。 (私のアカウントに書いてあることはもともと正しさの保証は一切できないのですが^^;)

目的

GoFデザインパターンの中で有名度トップ5には絶対入っているであろう「Observer パターン」ですが、そこに出てくる登場人物が個人的にはとってもわかりにくい!

やっていることは「何かが起きた時に、その何かを他の誰かへ通知する」というデザインパターンの中でもシンプルなほうなので、実装時にもよく使うんですが。
しかし、登場人物というか、その登場人物の呼び方がシチュエーションによってコロコロ変わるのが本当に厄介です。
例えば、GoF では Subject/Observer だったり、別名 Pub/Sub 方式と呼ばれ Publisher/Subscriber だったり、Rx では IObservable/IObserver だったり・・・

ということで、個人的な備忘録として GoF でいう Obesrver は、 Pub/Sub 方式では?Rx では?というのを書いてみます。

それぞれの比較

GoF の Observer パターン

まず、GoFデザインパターンで出てくるやつです。

「イベントを発行する人(= 発火元)」が Subject です。
「イベントを受信して処理する人」が、 Observer です。

上記の場合だと、シーケンス的には以下の感じになるでしょうか。

  1. あらかじめ、 Subject は、 Observer を登録しておく
    Subject#AddObserver の実行)
  2. Subject にて何かイベントが発生した場合は、 Observer へ通知する
    Subject#NotifyObservers の実行 ⇒ 内部では Observer#Update の実行)
  3. ObserverUpdate を受けて、何かしらのイベント処理を行う

言ってしまえば、Subject は観察対象、Observer は観察者ですよね。そのまんまですが。

Pub/Sub パターン

次に、GoF の Observer パターンはよく「別名 Pub/Sub パターンとも呼ばれる」と言われていますが、 Pub/Sub パターンでは Observer パターンの登場人物がどのように割り当てられるのか整理してみます。

と、その前にそもそも、「Observer パターン = Pub/Sub パターン」というのは厳密に言うと誤りらしいです。
基本的な考え方は同じなんですが、 Pub/Sub パターンでは、いわゆる Publisher と Subscriber の間に「Broker」なるものが介在します。この辺はこちらに詳しく書かれているので、省略します。

が、ここでは無理やり登場人物をあてはめます。

「イベントを発行する人(= 発行者)」が Publisher です。
「イベントを受信して処理する人(=購読者)」が、 Subscriber です。
「イベントをとりまとめる人(=仲介人)」が、 Broker です。とりまとめると言うとあやふやなので、「メッセージを受け取ると、購読登録者へ配達する人」みたいな感じでしょうか。

シーケンス的には以下になると思います。

  1. あらかじめ Subscriber は購読したいイベントの topic(イベント名みたいなもの)を、Broker に登録しておく
    Broker#Subscribe の実行)
  2. Publisher は何らかのイベントが発生した場合は、 topic と共に Broker へ通知する
    Broker#Publish の実行)
  3. Broker は、あらかじめ登録されている Subscriber に対してイベントを通知する
  4. Subscriber はイベントを受け取ると、何かしらのイベント処理を行う

つまり、Pub/Sub パターンを Observer パターンに当てはめた場合、以下のような対応になります。
イベント発生側:Publisher = Subjectイベント受信側:Subscriber = Observer

※ ただし、やはり Observer パターンとは異なり、PublisherSubscriber は基本的に依存関係にありません(つまり疎結合)。Publisher は自身が通知したイベントがどの Subscriber が購読しているか知りませんし、 Subscriber もイベントの発信元がどの Publisher なのかを知りません。

Rx (Reactive Extensions)

はっきりいって、 Rx はまったくの初心者というか勉強中の身なので偉そうなことは書けませんが、今自分が把握していること(というか理解したつもりでいること)を書きます。

もうまんまなのですが、 GoF の Observer パターンでいうと、IObserveable = Subject で、 IObserber = Observer ですね。(この時点で、なぜ名前を統一しないのか!GoF の定義をそのまま使ってくれ!と思っちゃいます1

そして、さらに恨み節ですが、「IObservableSubscribe メソッドを持つ」というのが個人的にわかりにくい。こんがらがる原因は Observer パターンが Pub/Sub パターンと関連しているという前提認識があるからなのだと思いますが、本来 IObserber が購読者であるはずなのに、 IObservableSubscribe するというのが混乱の元になってます。2

また、もっとやっかいなことに Rx では Subject という「 IObservableIObserver の両方を実装した、一人二役する」クラスがいます。
「おーい、IObservable = Subject やなかったんかーい」って、もうこの段階でパニックです。

まとめ

ということで、 Rx やるうえで、以下のことを覚えておきます。

パターン名 イベントの発行元(= 発行者)の呼び方 イベントの受信先(= 購読者)の呼び方 備考
Observer パターン Subject Observer
Pub/Sub パターン Publisher Subscriber
Rx IObservable IObserver ※ただし、IObservableIObserver の両方を実装する Subject というクラスがある

うーん、ややこしい・・・


  1. 一応補足しておくと、たぶん IEnumerableIEnumerator の絡みという理由があるのだろうと思ってます。

  2. Pub/Sub パターンでも、もし Broker が介在しなければ、 Publisher が Subscribe メソッドを持つことになるので、よく考えたらおかしくないのですが。