昨日のエントリの続き。こっちのほうが有益な情報になってると思うんだけど多分昨日ほどはのびない。
さて、昨日のエントリーでは「Backbone.jsのViewはControllerってことなのか〜それは俺が間違えてたわ〜、えっじゃあ Marionette.js 使う場合はどうなの」という感じになったのだけれど、そのあといろいろ考えて以下のような感じに落ち着いた。
Marionette.jsを使っていたとしても結局考え方はBackbone.jsのときとかわらない。
つまり、Marionette.js の View も C である。ViewControllerと言うべきかもしれないので以下ではViewControllerと書く。
ViewControllerの責務は、以下の通りである。
- Model(あるいはCollection)をひとつ保持し、View(HTML片のことである)をひとつ保持する
- Modelの変更イベントに応じてV(HTML片)をレンダリングする
- HTML上のイベント(クリックされたとかそういうやつ)を監視して、それに応じてモデルを操作する。
さて、ここまではよい。しかし、ここで Model の種類がたくさんある場合にどうすればよいかという問題が出てくる。昨日の例で言うと、「アーティスト一覧の中からアーティストをクリックするとレコード一覧を表示する」みたいなロジックをどうやって組んでいくか、という問題である。しかし、実はこれはきちんとモデルが設計されていれば問題にならない。
実際に書いてみた
で、口だけマンには死んでもなりたくないというプライドがあるので、実際に動作するサンプルコードを書いた。小規模なのに無駄に Marionette.js on Rails である。
https://github.com/Shinpeim/marionettejs_sample
Rails が担当してるのはAPIの部分。rake routes の結果は以下の通りで、
Prefix Verb URI Pattern Controller#Action artist_records GET /artists/:artist_id/records(.:format) records#index artists GET /artists(.:format) artists#index root GET / welcome#index
で、
GET /artists
でArtistの一覧をJSONで返すGET /artists/:artist_id/records
でそのアーティストのレコード一覧をJSONで返すGET /
はHTMLページをレンダリングする
となっている。
というのを前提として、実際に書いたクライアントサイドのコードは以下の通りである。
まずは、アーティストを表すArtist
モデルと、その集合を表すArtists
コレクションが存在する。また、レコードを表すRecord
モデルと、その集合を表すRecords
コレクションが存在する。そして、Artist
モデルは自分のレコード一覧を表すRecords
コレクションをfetchするfetchRecords
というメソッドを持っている。
さて、話はViewControllerのほうに移る。Artsits
コレクションを保持し、その情報をHTML上にレンダリングする責務を持っている ViewController が、ArtistsView
である。また、ArtsitsView
はHTML上のアーティストのどれかがクリックされたときに、そのイベントを拾ってArtists
コレクションの操作メソッドを呼ぶ責務も持っている。今回ならばまずは「実際にArtists
コレクションの中からArtist
を選択する」という操作に責務を持っている。
実際にその操作をしているのはArtistsView#artistSelected
メソッドで、その中で@collection.selectOne(artistModel)
しているのが見て取れるだろう。
さて、アーティストを選択したのはよいが、今度はそのアーティストのレコード一覧を表示するという仕事をしなければいけない。しかし、ArtistsView
はRecords
モデルを保持していない。じゃあどうするのか。ここでArtist#fetchRecords
の出番である。Model同士の関係をきちんとModelの世界で定義しておいてあげるのが大事で、これをCでやろうとかしたりするといわゆる「FatController問題」にハマりこんでゆく。
さて、首尾よくArtist
からRecords
が取得できたので、あとはこの引っ張ってきたコレクションを表示するための ViewController を ArtistsView
が保持しておけばよい。つまり、ViewControllerは下位の ViewController を持ちうるのである。
このように、ある ViewController が受け取ったイベントが二つ以上の異なった種類のモデルに関係する場合があるが、それらの異なったモデルは、なんらかの関係を持っているはずである。でなければおかしい。そういう、「ModelAをこうするとModelBがああなる」みたいな部分、これこそが「ビジネスロジック」であって、それを操作するのが Model の責務である。なので、ViewController はあくまでそのViewController が保持している Model の操作メソッドを呼び、「これがこうなったときはあれがああなった」という結果だけを受け取るべきである。その結果を別の場所にレンダリングしたいな〜とかそういうときには、「どのViewControllerがどのViewControllerのライフサイクルを管理するのか」をきちんと意識しながら、適宜 VC が別のVCを作ったりしてあげればよい。これはおそらくほとんどの場合、どのModelがどのModelに依存しているのか、という関係と一致すると思うので、結局Model をしっかり設計するのが大事だね、というあたりまえの結論になるのだった。
以上です!!!!!