読者です 読者をやめる 読者になる 読者になる

Backbone.jsでViewからModel・DOMを操作する時の流れ

自分の頭の中の整理のために。

はじめに

Backbone.jsガイドブックを読んでいたら、このような事が書いてあった。

Backbone流MVCでは、ビューとコントローラは両方ともViewが担当します。(略)これらはそれぞれViewのメソッドとして実装しますが、本書では区別のために前者をビューメソッド、後者をコントローラメソッドと呼ぶことにします。(略)重要な点は、DOMイベントに応じてコントローラメソッドが実行されることで処理が始まり、その中でmodelやcollectionを操作し、その結果生じるイベントがビューメソッドを呼ぶ、という流れを意識することです。そのためにも、コントローラメソッドには処理の起点になる以上の仕事を与えず、DOM操作はすべてビューメソッドに集めるよう明確に区別しましょう。

図にするとこんな感じになる(点線がイベントで実線が直操作)

しかし、なぜこのような書き方にするとよいのか?2つの疑問をもった

1. なぜコントローラメソッドとビューメソッドを切り分けるのか?
2. なぜコントローラメソッドがビューメソッドを直接呼び出さないのか?

以降、それぞれ整理してみる

1. なぜコントローラメソッドとビューメソッドを切り分けるのか?

まず、なぜわざわざコントローラメソッドとビューメソッドを切り分ける必要があるのか?逆に以下のように一緒にすると何が困るのか?

思うに、この書き方の問題は、DOMの更新を担当する部分のコードが再利用できないことにあると思う。例えば以下のように複数イベントハンドラが同じDOM更新を行う場合、上記の書き方だとコードが重複する。

DOM更新の部分を切り出して別メソッドにすれば再利用できる

役割が分離されているので、テストもしやすくなるし、理解もしやすくなる。

2. なぜコントローラメソッドがビューメソッドを直接呼び出さないのか?

では次に、なぜコントローラメソッドが直接ビューメソッドを呼び出さない方がよいのか?つまり以下のように書くと、なぜよくないのか?

恐らく、問題はModelとViewが1対多になった時に起こる。自分が作ったWebサービス音楽制作のためのWebサービス)でModelとViewが1対多になる部分が多くあったので例にあげる。例えばこんな部分。

この部分では音源を表す1つのModelに対して、再生ボタンを管理するPlayButtonViewと波形を管理するWaveformViewという2つのViewが存在している。例えば音源が停止している状態から再生ボタンと波形それぞれをクリックすると、以下のような動きをする。

・再生ボタンをクリック

  • > PlayButtonViewがModelを再生
  • > PlayButtonViewが再生ボタンを停止ボタンにする + WaveformViewが波形を動かす

・波形部分をクリック

  • > WaveformViewがModelを再生
  • > PlayButtonViewが再生ボタンを停止ボタンにする + WaveformViewが波形を動かす
ビューメソッドを直接呼び出すと

これをコントローラメソッドがビューメソッドを直接呼び出すように書くと、以下のようになる。

Modelに対してViewが2つであったとしても、既にかなり複雑になっている。これが3つ、4つとなってきたら確実に破綻する。

Modelのイベントを購読すると

では、これをビューメソッドがモデルのイベントを購読するように変更したらどうなるだろうか?図にすると以下のようになる。

Modelのイベントを介することによって、View1とView2が疎結合になった。DRYだし、理解しやすいし、テストもしやすい。

まとめ

もちろん、コントローラとなるViewのメソッドが必ずModelとDOMを更新するわけではない(ただDOMを更新するだけの時や、イベントを発火するだけの時など様々なケースがある)し、ViewとModelが複雑でイベント仲介者(Mediator)を置かないと厳しいこともあるが、「Model更新->DOM更新の基本的ケース」においては、上記のような書き方をするとだいたい幸せになれるように思った。