May 25, 2013
どんなに当たり前になった開発手法やプログラム管理方法があっても、新人さんにとってはHello worldから入って行くと思う。インターネットで、「知の高速道路」が整備されたと言っても、意外と古い話を探すのは難しい話で、MVCみたいに当たり前になってしまったことについて、何故そんなものが存在するのか?という考え方を後から肌で感じるのは難しく、そんなことを考えていたら、突然MVCについて書きたくなった。
以下、書いていくがかなりの偏見が入っている気はするので、ぜひ、歴史認識が間違ってたら僕のためにツッコんでくださいませ。
僕がMVCアーキテクチャを知ったのは、JavaのServletを勉強していた時だった。Javaはオブジェクト思考で作られている言語かつ、Webに特化した言語ではないため、クラス間のデータは、インターフェース仕様に基いて秘匿されるのと、テンプレートエンジンは別に存在していたので、PHPのように一つのファイルで、SQLの処理からビューの処理までを書くようなプログラムは、基本的に書けなかった。
(なおJSPがデバッグ性の面で、まだ使いにくかった時代の話をしてる。昔、全てのエラーがexceptionでもみ消される動きになってたし、ダイナミックなクラス入れ替えがうまくいかなかった記憶があるし、当時何よりEclipseでJSPにはブレークポイントが設定できなかった記憶があるので、仕様上ぬるぽが出れば一発退場のJavaでは、結局モデルクラス側でテストをしっかりやらないと、画面を動かしながらデバッグするのは非効率だったので、インターフェース仕様を定義した上で、クラス個別にテストする方が、よっぽど効率的だった。)
各機能(オブジェクト)の役割は厳密に定義され、オブジェクト間のデータのやりとりは、アクセサメソッドを通じて厳密に定義されなくてはいけなかった。データベースにアクセスする機能や、Webにデータを返す機能をそれぞれクラスとして分離しなくてはプログラムを書くことができない。
いきなり脱線するが、すべてのアクセスで共有するような箱のようなオブジェクトは簡単に作れる。Staticに宣言しておけば単一のデータソースを作れる。しかし、それはJava的に楽にアクセスできるものであって、Webサーバーとしては優しくない作りになってしまう。それはなんと、全てのWeb上のアクセスで共有されるオブジェクトになってしまうからだ。もし、そこに、ユーザーデータを入れた瞬間に情報漏えいする。過去に、とある有名オンラインストアで、Static変数にユーザーデータをツッコんで情報漏えいをやらかした記憶があるが懐かしい想い出だ。
つまり、そういう手抜きみたいな記述法は仕様上に許されておらず、しっかり作りこむ必要がある。
一言で言うと面倒くさい。今となっては相当面倒くさい。
しかも、PHPのfile_get_contentsのように、たった一行でWebにあるHTMLを引っ張ってくるようなリッチな言語ではなく、当時は、ローカルファイル一つ開くのに、IOストリームから処理しなくてはいけないというプリミティブな言語なので、割と機能を整理し、しっかり設計しないとワケがわからなくなるため、この中で「オススメの設計の仕方」が、他の言語のパターンを参考にした、MVCアーキテクチャだったと認識している。
MVCが一番、美味しいと思う所は、
1.「モデル」を、「画面の表示方法(ビュー)」とはあえてコードを切り離すことで、モデル自体の再利用を促す。
例えば、PHPなどを使ってる時に、エイヤー!と、画面ごとにデータベースにアクセスするコードを都度都度書いていると、テーブルの仕様が変わった時に、関連する全ての画面を一つ一つ置換していかないといけない。そこでは当然、反映漏れも含めて人的ミスが発生しやすくなるが、データを担保している部分を「モデル」だけに切り離すことで、データベースに通信しているロジックが少なくなるので、変更の手数を最小限にする。
その変わり、開発時には、「モデル」から取得したデータを「ビュー」に変換するコードを都度都度書かなくてはいけない。
例えば、ツイートの日付を管理するとして、ある画面ではYYYY/MM/DDで表示し、ある画面では、「1分前」で表示しようとした時に、モデルはDateオブジェクトを返し、ビューのレイヤーではそれを適切な表示方法で加工するためのテンプレートのメソッドを作成する。この連携作業をそれぞれ書くことで、再利用性を高める作用が産まれる。
ここでエイヤーと作りたい人は、「なんでそんな面倒くさい事をするの?別にその都度のコードを書けば良いじゃん」と思う人もいるだろう。これは、上に書いたことをやりたいと思わない環境にいるのであれば、間違いなく正しい。これは卑下しているのではなく、そんなことを知らなくてもWebサービスで、お金持ちになった人を知ってるので、本気でそう思っている。
そもそもWebサイトの場合は、実は言うほどモデルの再利用性は高くない。「ツイートの日付」を例にとったが、そういう共通パーツというのは、「Webサービス」のコアデータではよく使うが、CMSのような「Webサイト」となると、実はそんなに多くない。
それはWebが本質的にプレゼンテーションロジックだからであって、ECならバックエンドは人力だし、基幹システムがあれば、そっちがモデルだったりして、ビジネスロジック全体の中でのWebサーバーそのものがビューの役割だったりするからってのはあるだろう。
画面自体が冗長になるのは画面設計がおかしいのだから、基本的には画面毎に呼び出すデータも変わることが多い。例えばjoinを使うならjoinの仕方やjoinするテーブルの数が画面ごとに変わる。つまりモデルに関わる機能は、ビューに対応してリニアに増えていくことが多い。これを一つのメソッドでまかなおうとすると、引数毎の分岐が増えてしまったりして見通しが悪くなるので、サービスの中核を担うようなロジック以外は、モデルのメソッド自体を増やしていく方がエレガントであることが多い。
こういう疑問に対する回答としては、ツッコミたくなる前提を認めた上で、MVCがトレンドになって、メジャーなフレームワークがその作法に従って作られてるので、フレームワークについてる便利機能を享受したければ「作法に従え」というのが回答の一つになる。
もっとエレガントな回答については、はてブコメントででもいただけると助かる。
2.元来のコントローラーの役割について。
PHPやcgiなどは、構造上、Webサーバーと密着し、URLとプログラムが1:1に対応するようにできている。○○.phpや○○.cgiは一つのプログラムを表す。故に、基本的にURLの数だけプログラムが一つは存在するというのが当たり前だったのではないかと思う。
ところがServletの場合、サーバー機能を成しているJavaサーバー(Servletコンテナ)自体が、JavaのプログラムであることもありURLに対応する動作というのは、割と自由に扱うことができた。おそらくWindowsアプリなどからWebを始めたばかりで、かつ、仕様書プログラミングを強制されるSIerさん方面で、こういうオレオレプログラムが結構あった。
/index?cmd=listindex&.....
/index?cmd=listview&.....
/index?cmd=post&.....
/index?cmd=edit&.....
つまりWebサーバーからのエントリポイントは一つにして、cmdのような引数で分岐していくようなプログラムだ。いかにもexcelで書きやすい仕様だと思う。(VBでも似たようなプログラムを見たことがある。何故かボタンの押先が一つのプログラムにまとまってるという。。。)
MVCの「コントローラー」というのは、本来はこういうレベルでの呼び出し制御を担うものだったと僕は認識している。
この分岐の後には、URLに対応した処理を記述するが、そこでは、
・validation処理を呼び出し。問題があったらエラー表示するよね。
・処理ロジックをモデルと連携して呼び出し
・あと、これとこれとこれとこれを実行すれば、画面が生成できますよね、
などを書くが、この旗振り役を行うのがコントローラーである。
Javaの場合、クラス間のアクセスが厳密なので、コントローラーがデータそのものを操作するのは基本的にやらない。蓋を開けて中身を操作する人が増えるとバグの発生箇所が増えるからだ。工場で流れているたこ焼きを、ラインの管理マネージャーが蓋を開けて食べてしまうこともできるが、バグがあった時に、たこ焼きを焼く人を疑っていたのに、実はマネージャーが食ってましたというのは、コンプライアンス上非常によろしくない。そういうことは普通起きないようにする。
故にコントローラーは、データのロジスティクスと、データ処理をする担当者のマネジメントに専念するためのクラスである。・・・であった。
なお、/index?cmd=xxxxのようなURL仕様は、RESTの概念を知る前から気持ち悪かったのを覚えている。
URLを抽象化することで、URLが変わった時に仕様変更しやすいという、モデルと同じ理屈は成り立つのだが、そもそもURLの変更なんて、データベースの仕様変更と比べると、そんなに頻度が高いものではないし、コードからURLがわかりにくくなることを考えても、優先されるファクターではない。
何よりWebのアーキテクチャが蔑ろにされている感が強かったが、それはRESTの概念が出てきてはっきりしたし、Googleが出てきたことでSEOとも相性が悪いことがわかったので急速に廃れていく。
そのトレンドと連動したのかはわからないが、最初はXMLファイルでURLとクラスとを自動マッピングする仕組みが提案され、最近は、もっと柔軟かつ複雑なURLとの対応関係を記述することができる。既に気がついてると思うが、フレームワークと呼ばれるプロダクトの中核になるルーティングの概念がそれに当たる。
長々と書いたが、ここまではMVCが綺麗に整理されていた、と僕は思っているのだが、それはJavaというオブジェクト指向言語において、MVCパターンは必要とされるべく存在したからだと思う。
しかし、この後、PHPなどのLL言語にMVCが導入されるという本末転倒感を伴った時代の変化が訪れる。
ここでCの役割が曖昧かつリッチになっていくのだが、MVCが多くのWebサービスにとっては、若干オーバースペックだったからというのが個人的解釈だ。結果、フレームワーク作者によって解釈がぶれる形になりしばらく業界が迷走することとなる。その戦国時代に対する一つのエレガントな答えを提供したのが、Ruby on rails卿ということになるが、MもCもWebに合わせて役割を再定義したというのが正しいのかもしれない。その話はその気になれば書くかもしれない。