昨今のフロントエンド技術は進歩が圧倒的に早く、
jQueryわかります!MV*フレームワーク使ったことあります!
という程度ではもはや技術についていけなくなってしまっているこの頃です。
技術に追いついていけるだけの基礎体力をつけるべく、GUIプログラムにおけるMVCパターンのデザインパターンについて学習したことを書いていきます。
まずはObserverパターンから…
と思ってGUIにおけるMVCをいろいろ調べていたのですが、必須なのはObserverパターン(とその派生)だけだったという結論に至ったのでこれ以上javascriptのデザインパターンについては投稿するのやめました。
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
各クリックイベントは、対象を知る必要がなくなり、
状態の通知のみを行えばよくなりました。
イベントの対象と発行が綺麗に分離できましたね