Edited at

javascriptでObserverパターン

More than 3 years have passed since last update.

昨今のフロントエンド技術は進歩が圧倒的に早く、

jQueryわかります!MV*フレームワーク使ったことあります!

という程度ではもはや技術についていけなくなってしまっているこの頃です。

技術に追いついていけるだけの基礎体力をつけるべく、GUIプログラムにおけるMVCパターンのデザインパターンについて学習したことを書いていきます。

まずはObserverパターンから…

と思ってGUIにおけるMVCをいろいろ調べていたのですが、必須なのはObserverパターン(とその派生)だけだったという結論に至ったのでこれ以上javascriptのデザインパターンについては投稿するのやめました。

MVCについては、下記投稿や、

smalltalkのMVCについて目を通しておけば大丈夫だと考えます。

MVC、本当にわかってますか?

smalltalk mvc


投稿者のバックグラウンド

参考までに書いとく。


  • WEBアプリケーションのサーバサイドプログラムメインに開発

  • フロントエンドはMV*フレームワークなど利用できるが、概念までは説明出来ないゆるふわ系

  • GUIプログラムはオブジェクト指向のない言語でなら経験あり


Observerパターンとは?

イベントによって状態を更新するObserverと、そのObserverの登録や削除、イベントの通知を行うSubjectから構成される。

Subjectは登録されたObserverに対して通知を行うだけでよく、

Observerが何であるかということに関心を持たなくて良い。

と小難しく書いたが、要はSubjectとObserverに分けることでイベントの対象とイベントの発行が疎結合に出来るデザインパターン。

他に、Observerの状態に一貫性を持たせることが出来るという効用もあるようです。


サンプルプログラムで見てみる

ボタンをクリックするとボックスのカラーが変わるというプログラムを想定。

ボタン1をクリックすると赤に、

ボタン2をクリックすると青に、

ボタン3をクリックすると赤に変わって青に変わる。


Observerパターンを使わないプログラム

btn1 = document.getElementById 'btn1'

btn2 = document.getElementById 'btn2'
btn3 = document.getElementById 'btn3'

box1 = document.getElementById 'box1'
box2 = document.getElementById 'box2'

btn1.onclick = () ->
to_red = 'red'

box1.style.backgroundColor = to_red
box2.style.backgroundColor = to_red

btn2.onclick = () ->
to_blue = 'blue'

box1.style.backgroundColor = to_blue
box2.style.backgroundColor = to_blue

btn3.onclick = () ->
to_red = 'red'
to_blue = 'blue'

box1.style.backgroundColor = to_red
box2.style.backgroundColor = to_red

setTimeout () ->
box1.style.backgroundColor = to_blue
box2.style.backgroundColor = to_blue
, 500

各クリックイベントでは、イベントの対象となるオブジェクトを知っていなければ処理が書けないことが分かります。


Observerパターンを適用する


SubjectとObserverのオブジェクトを定義

class Subject

_observers = []

add: (observer) ->
_observers.push(observer)

notify: (context) ->
for observer in _observers
observer.update(context)

class Observer
update: (context) ->


  • Subject

Observerの登録と、登録したObserverに対してイベントの通知を行う。

(本当はSubjectオブジェクトには登録したObserverを削除するメソッドも必要ですが、今回のサンプルでは利用しないので省略しています。)


  • Observer

自身の状態を更新するメソッドを持ちます。


継承用関数の準備

上で定義したSubjectとObjectも抽象オブジェクトとして利用します。

そのため、継承用の関数を定義します。

extend = (obj, extension) ->

for key, prop of obj
extension[key] = obj[key]


実際の処理

btn1 = document.getElementById 'btn1'

btn2 = document.getElementById 'btn2'
btn3 = document.getElementById 'btn3'

for btn in [btn1, btn2, btn3]
extend new Subject(), btn

box1 = document.getElementById 'box1'
box2 = document.getElementById 'box2'

for box in [box1, box2]
extend new Observer(), box

box.update = (color) ->
box.style.backgroundColor = color

for btn in [btn1, btn2, btn3]
btn.add box

btn1.onclick = () ->
this.notify 'red'

btn2.onclick = () ->
this.notify 'blue'

btn3.onclick = () ->
self = this

self.notify 'red'

setTimeout () ->
self.notify 'blue'
, 500

各クリックイベントは、対象を知る必要がなくなり、

状態の通知のみを行えばよくなりました。

イベントの対象と発行が綺麗に分離できましたね :blush:


Observerパターンの派生

出版-購読型モデル

イベント駆動型プログラミング