はじめに
こんにちは。今年の3月からKRAYに入社した阿部です。
ブログには初登場になります。
今日は、昨今のアプリケーション開発では誰もが耳にしているであろうMVCパターンを取り上げます。(以下MVCと呼びます) 開発者それぞれで理解や解釈が違っていることが多く、しばしば議論を呼び起こします。「ぼんやり」と理解したままの方も多いのでは無いでしょうか? 私もある程度、開発で実践してみるまでは、なかなか良い形でMVCを適用することが出来ずにいました。皆様のMVCへの理解を少しでもクリアに出来れば幸いです。
定義をおさらい
MVCは図で示されることが多いですね。
Wikipediaを見るとMVCの典型的な相関図が掲載されています。
(Wikipedia日本語版 Model View Controller より)
Wikipedia英語版にも掲載されているこの図ですが、かなり上のレイヤから見た考え方を示したものと思います。これだけでは実装に落とし込むのは難しいですね。
少し読み進めてMVCの構造に進むと少し違った構成図が掲載されています。
Model-View-Controller 概念的な構成図。直線は直接的なAssociationを表し、破線は (例えば)Observer パターンを経た間接的なAssociationを表す。
(Wikipedia日本語版 Model View Controller より)
依存関係も示されて、少し具体的になりました。
続いて、WikipediaのMVCの構造よりMVCの各要素の定義について抜粋します。
Model
そのアプリケーションが扱う領域のデータと手続き(ビジネスロジック – ショッピングの合計額や送料を計算するなど)を表現する要素である。また、データの変更をviewに通知するのもmodelの責任である(modelの変更を通知するのにObserver パターンが用いられることもある)。
多くのアプリケーションではデータの格納に永続的な記憶の仕組み(データベースなど)が使われている。MVCの概念では、データの(UI以外の)入出力は取り扱わないので、データアクセスも本来MVCの概念の範疇を超えるものではあるが、あえて言えばmodelの中に隠蔽されると考えられる。
View
modelのデータを取り出してユーザが見るのに適した形で表示する要素である。すなわち、UIへの出力を担当する。例えば、ウェブアプリケーションではHTML文書を生成して動的にデータを表示するためのコードなどにあたる。
Controller
ユーザの入力(通常イベントとして通知される)に対して応答し、それを処理する要素である。すなわち、UIからの入力を担当する。modelとviewに変更を引き起こす場合もあるが、直接に描画を行ったり、modelの内部データを直接操作したりはしない。
理解出来ました?
さて、ここまで標準的なMVCの概念について紹介させていただきました。ですが、これらの説明だけではピンと来ない方、多いと思います。
なぜでしょう?
ステートレスなWebアプリケーション開発をメインにしている方にとっては、構成図がしっくり来ないと思います。きっとその原因は、Wikipediaにあるこの一文です。
元来Smalltalkにおけるウィンドウプログラム開発のための設計指針として生まれたが、構造が複雑となりがちなグラフィカルユーザインターフェース (GUI) をもつソフトウェアにおける有用性から他方面へ広がった。
MVCは状態を持つイベント駆動型のクライアントアプリケーションのために生まれた開発手法です。Ruby on RailsなどのWebアプリケーションフレームワークにおける、Model-View-Controllerの区分けは、MVCに大きく影響を受けていると考えられますが、元来のMVCとは異なります。簡単に違いを言ってしまえば、ステートレスなサーバーサイドのMVCには「Observer パターンを経た間接的なAssociation」いわゆるコールバックがありません。
一方、Backbone.jsの様なクライアントサイドのフレームワークは、元来のMVCの考え方がしっくり当てはまります。イベント駆動だからですね。
クライアントアプリ開発をメインにしている方にとっては、MVCの構造の説明、特にViewについてはサーバーサイド寄りの説明なので、これまたピンと来ないと思います。
以降、本稿ではクライアントサイドのMVCをメインに考えていきます。ですが、サーバーサイドとクライアントサイドではMVCは全く別物か?というと、そうとも言えません。共通する考え方は多分にありますので、サーバーサイドエンジニアの方もどうかお付き合い下さい。
なぜMVCを使うのか?
最初にMVCを適用する理由について考えてみましょう。いわば、目的・メリットですね。「なんとなく。良いって言われているから。流行っているから。」ではいけません! 今回、ブログ執筆にあたり、この部分を自分でも再確認したくて、Wikipediaで紹介されているSmalltalkでの最初の論文に目を通してみました。
(A Description of the Model–View–Controller User Interface Paradigm in the Smalltalk-80 System より)
そこにはこうあります。
This strategy was chosen to satisfy two goals: (1) to create the special set of system components needed to support a highly interactive software development process, and (2) to provide a general set of system components that make it possible for programmers to create portable interactive graphical applications easily.
(1)に、”highly interactive”という言葉があります。直訳すると”高度に相互作用的な”といった感じでしょうか? これは、クライアントサイドで重要になってきます。質の高いインタラクティブなアプリケーションを作るには複雑な状態を管理し、それをコントロールできなければいけません。質の高いインタラクティブなアプリケーションの開発を支援するのがMVCの一つの目的と捉えました。
(2)について、前半はフレームワークのことを言っていますね。後半の”portable”が重要だと思います。アプリケーション開発における”portability”とは移植性のことです。開発言語の違いもあるので、まるまる違うプラットフォームに移植できるように実装するのは難しいです。ですが、次の章のタイトルには
MVC and the Issues of Reusability and Pluggability
とあります。
移植性の高いプログラムは必然的に”Reusability and Pluggability”を備えなければいけません。つまりは、UI以外の部分を再利用可能にすることがもう一つの目的です。
続く本文からは違った視点の目的も拾えました。
Isolating functional units from each other as much as possible makes it easier for the application designer to understand and modify each particular unit, without having to know everything about the other units.
これは、Viewについて言っていますね。UI部分のコードをデザイナーさんが理解しやすく、詳細を知らなくても変更できるようにすることも目的の一つだと思います。
それでは、上に挙げた目的を達成するためには、具体的に何をすれば良いのか?
大切なことを考えていきます。
大切なこと
アプリケーションの手続き(ビジネスロジック)はModelに詰め込む
WikipediaのMVCの構造で述べられていることそのままなのですが、つまりはModelはデータだけでは無いということです。これについて解りやすいのは上に挙げたSmalltalk論文の図です。
(A Description of the Model–View–Controller User Interface Paradigm in the Smalltalk-80 System より)
ポイントはModelの中に書かれている 「state and behavior」 つまりはModelの中に状態と振る舞いがあることです。
実際にMVCを用いた開発をしていてよく目にするのは、ControllerがModelのデータを直接変更する様なコードです。すぐにControllerが肥大化して収拾をつけるのが大変になってきます。また、手続きがControllerに書かれることでModel再利用のメリットも減ってしまいます。Modelには手続きを行うI/Fを用意して、Modelのデータは手続きにともなってModel自身が変更する様にするとControllerがコントロールに専念できるようになります。
ModelはViewにもControllerにも依存させない
再利用性を考えればModelはViewにもControllerにも依存させないようにしなければいけません。視点を変えるとViewとControllerはがっちりModelに依存します。アプリケーションが表現する内容は全てModel側で用意します。Modelは大きくなりますが、アプリケーションドメインは大きくて当然です。
コールバック
上で述べたようにModelからView/Controllerへはソースコードのシンボルレベルで依存しないようにします。では、Modelの変化をきっかけにディスプレイの出力に反映させたい場合はどうするのでしょう? ここで登場するのがお馴染みのObserverパターンです。Observerパターンの詳細は割愛しますが、重要なことは2点、コールバックで依存関係をView→Model、Controller→Model へと一方向にすることと、Modelに起きたことがView/Controllerに通知されるようにすることです。
Wikipediaの構成図ではModelからControllerへのコールバックが示されていないのですが、クライアントサイドではModelからControllerへもイベント通知を行う必要性が出てくることがほとんどだと思います。例えば、以下の様なケースです。
- エラー発生時
- Modelの手続きの結果、ユーザーの選択が必要な時
- なんらかしらの手続き完了に伴い画面遷移する時
Viewは表示更新に専念させる
注目していただきたいのは、Wikipediaの構成図にあるViewからControllerへ伸びる破線です。同じくコールバックなのですが、ユーザー入力を受けたViewはControllerにユーザー入力の処理を委ねます。なぜ、Viewから直接Modelへの手続きの指示はせずにこんなまわりくどいことをするのでしょう? 理由は2つあると思います。
- アプリケーションのコントロールに関わるコードをControllerに集中させる
- UI部分のコードをデザイナーさんが理解しやすく、詳細を知らなくても変更できるようにする
ViewとControllerは分けられない?
Wikipedia日本語版では以下の一文があります。
UIにおける入力と出力は本質的には不可分なものであり、したがってviewとcontrollerはいつでも分離できるとは限らない
例えば、iOSやAndroidでも普通に作るとM-VC(※)の構成になると思います。この構成でもModelへの詰め込みがしっかり出来ていれば大抵なんとかなるのですが、Viewが多い時はVCで全て頑張ろうとすると肥大化して大変になってきます。そういう時はカスタムViewを作ってViewとControllerを分けましょう。カスタムViewにModelの参照を渡して、Model監視→表示更新のコードを実装する形です。開発環境の違いはあれど、少しの工夫でControllerとViewの役割を分離することはできると思います。
※ UIViewControllerやFragment/ActivityなどがVCの役割と捉えています
まとめ
ここまで、私なりにMVCにおける大切なことを考えてみました。改めてModel-View-Controllerの役割を整理してみます。
Model
- 手続き全部(ビジネスロジック)
- データ操作
- 状態管理
- 変化をView/Controllerに通知
View
- 表示の更新
- ユーザー入力をControllerに伝搬
Controller
- ユーザー入力の受付
- Modelに手続きを指示
- アラートや選択ダイアログの表示
- 他画面への遷移
※MVCは歴史の長い開発手法であるとともに、多方面で用いられることから、正解の形は一つではありません。あくまで理解のための参考として頂ければと思います。
上に挙げた役割はクライアントサイドのMVCを想定していますが、多くのプラットフォームで共通して言えることは
- UIから切り離せるものは極力Modelに詰め込む
- Viewには手続きに関連するコードを書かない
ということではないかと思います。
また、インタラクティブなクライアントアプリケーションは状態を持ちます。UIに関連しないアプリケーションの状態をModelでがっちり管理することでControllerはユーザーとのやりとりに専念することが出来ます。
そして、一番大切なMVCを適用する目的についておさらいします。
- 質の高いインタラクティブなアプリケーションを開発する
- UI以外の部分を再利用可能にする
- UI部分のコードをデザイナーさんが理解しやすく、詳細を知らなくても変更できるようにする
最後に
以上、MVCについて考えてみました!
「ぼんやり」していたものがクリアになった方がいれば幸いです。
このエントリーに対するコメント
日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)
- トラックバック
-
- 初めてのフレームワーク-Laravel5-1基本用語整理 - IT記事まとめ2018/06/13, 12:26 AM
[…] とりあえず、https://kray.jp/blog/think_about_mvc/ […]
「いいね!」で応援よろしくお願いします!