SSブログ

MVCのコントローラは頭脳役か? [隠れた頭脳役]

さて、今回のテーマはMVCである。
これは非常に重要なテーマだ。
何しろGUI全盛の今日、MVCが絡まないプロジェクトの方が珍しい。
webアプリのフレームワークでも軒並み採用しているし、iOSアプリを作る時にも(Objective-Cネイティブでやるなら)避けて通れない。
そのわりに「そもそもMVCとは・・・」という文章には滅多にお目にかからない。
開発者の中にはワケが分からないけど仕方ないから使っている人とか、面倒くさいから嫌いと言う人も多いと思う。
もちろん原典と呼ぶべき資料はあるが、その手のお堅い資料を初心者で読む人は稀だし
そもそもMVCの概念も時代とともに変化しているから原典を読めば良いと言うものでもない。
だったら新たに書こうじゃないか。

それに加えて「Controller≒頭脳役」が名前にも入っているのだから「ソフトウェア工学には頭脳が足りない」シリーズにとっては格好のネタだ。ソフトウェアの頭脳役とは何かを説明するにはちょうど良い題材だろう。これを書かない手はない。

また、BSP(Brain, Skills, Physical)メタファの適用事例としても打ってつけだ。このMVCとBSPメタファの関係が理解できれば、擬人化設計技法を実務で使える様になるだろう。

さて始めようか。

MVCとは何か?
MVCモデルは大抵の人にとって、今やフレームワークのことを指す。
webアプリケーションですらフレームワーク上で動く様になって久しい。
フレームワークを使えば楽に開発できるが、その仕組みや設計思想を理解する機会はその分減っている。
しかしながら全体の動きを知らずに良い実装はできない。
だから今回はMVCフレームワークの仕組みを覗く事から始める。

知っている人も多いだろうが、まずMVCの定義を復習しよう。

MVC001.jpg

MVCとはGUI(グラフィカル・ユーザ・インタフェース)に統一感を持たせるための概念である。
扱う内容は違っても、扱い方が同じなら、同じ操作感を持たせた方が分かりやすい。
例えばボタンはボタンだし、ポップアップメニューはポップアップメニューだし、スクロールバーはスクロールバーだ。
これを実現するためには、まずGUIを他の実装から切り離して再利用可能なパーツにする必要がある。
他にもMVCの元となった思想はあるが、今回はこの視点を中心に見ていこう。

名前の元となった3要素は以下の通りだ。
V=ビューは文字通り「見た目」だ。
先ほどのボタン、ポップアップメニュー、スクロールバーなどのGUI用のパーツ、ウィンドウ等のアプリの外観を構成する要素のことである。これらは次の「Model」に見た目と操作感を与える役割を持つ。
また、クリックイベント等を内部に向かって発信する。

M=モデルはアプリケーションの本質を表している。
元々、モデルには2つの意味がある。
1つはデータ構造、
もう1つはビジネス特有のロジックである。
これはクラス内に変数と関数があるのと同じで、ある種オブジェクト指向的な発想である。
ただしビジネスロジックにも共通性があって、それらも再利用可能なように作るのが最近のトレンドなので、データ構造のみを指すことも多い。

さて、ViewとModelの役割は分かりやすいので、こんな大雑把な説明でもだいたい分かってもらえるだろう。
問題はControllerだ。

C=コントローラは色んな説明のされ方をしていて、概論からは実体が掴みづらい。
例えば「移植性が低く、再利用ができない、使い捨ての部分」と言われてもピンとこないだろう。
MとVをコントロールしている事だけは確かだが、
何をどのようにコントロールしているのか、今ひとつ掴みきれない人も多いのではないか。

MやVとの関係から想像できるCの責務は
M→V、つまりアウトプットと、
V→M、インプットの制御だ。
実際、この2つはコントローラの重要な責務だが、これだけなら仲介役であって制御役ではない。
アウトプットやインプットの途中で加工をしているとも考えられるが、これも加工役もしくはサービス提供役であって制御役ではない。
ではコントローラをコントローラたらしめているのは何だろう?

推論ではこの辺にして
ここからは具体的なフレームワークの挙動を見ていく事にしよう。
色んなMVCフレームワークがあるが
web用だと分散処理等の問題で話が複雑になるので
まず例としてiOS SDKを取り上げる。
(ちなみにAndroidじゃない理由は私がiPhoneユーザだから。)

例として扱うGUIの見た目はこんな感じだ。

Screenshot 2011.07.11 16.33.33.png


表(テーブル)形式の表示だが、これをiOS SDKではUIKitという標準のフレームワークで実現できる。
以下の図の「TableView(正確にはUITableViewクラス)」がそれだ。

MVC002.jpg

TableViewは「TableViewController」というコントローラを使って制御する。
Viewコントローラは名前が示す通りViewをコントロールするものだ。
ほかにコントローラと呼ばれる物が無い事からMVCのCは実はViewコントローラの事であると分かる。

TableViewに反映される元データで、もっとも単純なのは配列データだ。
配列に格納された個々のオブジェクトがテーブルの一つ一つのセルに相当する。

Viewコントローラの責務
さて、全体像が分かったところでいよいよViewコントローラの内容だ。
Viewコントローラは「元データ(モデル)に対する操作」と「表示の制御」という大きく分けると2つの責務を持っている。これは先ほど述べた「インプット、アウトプット」に相当する。このうち「元データに対する操作」は別にデータ・マネージャ(データ専用のサービス提供役)を立てて、そちらに委譲するのが普通だ。
だからViewコントローラのメインの責務は「表示の制御」である。
だから名前にもViewControllerと付く。

「表示の制御」はさらに「表示プロセス制御」と「インタラクション(ユーザの操作に対する反応)制御」に分けられる。
まずは「表示プロセス制御」だ。
例えばViewであれば、いずれも
「initilaize(初期化)→viewWillLoad(ロード前)→viewDidLoad(ロード後)→viewWillAppear(表示前)→viewDidAppear(表示後)」というプロセスを経て画面要素が描かれる。
プログラマからはプロセスの中のピンポイントしか見えないが、裏ではこの様に手順の決まったプロセスが回っている。
さらにTableViewであれば表を描画したりスクロールさせたりする処理プロセスが回っている。

一方、インタラクション処理も通常はViewからの入力に反応するメソッドが決まっているので、普通の関数(メソッド)との区別が付きづらいが、裏ではイベントが発生し、そのイベントに対するオブザーバ(イベントリスナーとも言う)を呼び出す処理が行われている。

この、外にはピンポイントでしか見えない「表示プロセス制御」と「インタラクション制御」がViewコントローラの実質的な二大責務であり、コントローラをコントローラたらしめている要素だ。この2つを一般化すると「プロセス制御」と「イベント処理」ということになるが、これらは常にコントローラ(制御役)と呼ばれる物の二大責務である。

余談だが私はプロセス制御が苦手だ。処理が複数のメソッドに分散されていると実行順を間違えてバグを仕込む事がよくあるが、時々、同じメソッド内でも順番を間違えて凹む。静的モデルや並列処理の方がよっぽど楽だ。こうした得手不得手は誰にでもあると思うが、昔に比べるとプロセス制御が苦手な人が増えているのではないだろうか?

BSP視点でMVCを捉える
次にようやくBSPの登場だ。これまで何度も出てきた様に、BSPは人のメタファであり、
「Brain(頭脳)、Skills(技術)、Physical(フィジカル)」の略で
ソフトウェアの各要素をこれらに当てはめて考えるための思考用フレームワークだ。
MVCではコントローラがBrain、つまり頭脳役と思うだろうが、話はそんなに簡単じゃない。(だからこそ面白い)

まず、これまでほとんど取り上げてこなかったPhysicalについて述べよう。
MVC中のPhysical要素とは何だろうか?
名前からしてコントローラではなさそうだが、M?V?どっちだろう?
これまでの回を読んでいれば気付くと思うが、じつは両方ともPhysicalだ。
Mはファイル化して持ち運べるし、Vは見たり触ったりできる外観を提供する。
だからどちらもPhysicalだし、むしろ両方揃ってこそPhysicalであると言える。
この混同こそがPhysicalの面白さなのだが詳しくは後程述べる。

つぎはS要素に行ってみよう。
結構ありがちなのがS(スキル、サービス)をコントローラ内に書いてしまう事だ。
これが一番手軽なので、単純なアプリを手早く作るのには向いているが
画面間で重複が出たり(ViewControllerは画面別に書く)
プログラムの見通しが悪くなったりするのでS要素は独立させるべきである。
大抵のフレームワークはそのための仕組みを持っていたりするので、それを有効に使った方が良い。
かつてはModelにビジネスロジックを持たせる事も多かったから、S要素をModelに書き込む事も多かったが、今日ではサービスとして独立させることは常識に成りつつある。
例えば先ほど出てきたデータ・マネージャは(広義の)Modelでありサービス提供役(=S要素)でもあるが、これに他のビジネスロジックが混じっていたらどう思うだろうか?たぶん気持ち悪く感じることだろう。

SをMVCから分離して書いたとして、さて、残るはBだ。
Brain要素はViewコントローラと同様に「プロセス制御」と「イベント処理」を責務とするが
それ加えて「推論」や「履歴管理」を行うことがある。「テキスト入力予測」などが身近な「推論」の例で、上手く使えばソフトウェアに大きな付加価値を付けてくれる。「履歴管理」は「操作の取消や再実行(アンドゥ、リドゥ)」に使われるし、商取引などで証跡を残さなければならない時にも使う。
さて、これらをどこに書こうか?
例えば、先ほどの「テキスト入力予測」や「履歴管理」だと処理の規模が大きいので「サービス提供役」に実際の処理を委譲するだろうが、ではその委譲のコードはどこに書くのだろうか?

ありがちなのはこれまたViewコントローラに実装する事だ。
でも思い出してほしい。ViewControllerはビューのコントロールのためにある。
目的を限定する意味で、BはViewコントローラと分けるべきだ。
でもまぁViewControllerに書くのが手っ取り早いからアプリが小さいうちはそれもアリだろう。
ただし、ViewControllerにアプリの頭脳役を任せたとしてもBrain要素の中心はMVCの外にある。

iOSアプリならApplicationDelegate やNotificationCenter と言った、もっと上位の機構があるし
他のフレームワークでも隠れている上位機構がたくさんある。
これらも立派なBrainである。
例えば自分で独自イベント処理を実装する場合、イベントループやイベントキューなどを司るシステムに支えられているし、
ネット環境で分散処理をするならロードバランサーなどたくさんの高度なシステムが裏で支えている。
こうした物を直接触る機会はほとんど無いだろうが、これらを意識しないとシステムのBSPを捉える事はできない。

過大評価されがちなMVC
さて、全要素が出揃ったところでもう一度MVCとBSPの関係について考察しよう。
以下の図はアプリのBrainにNotificationCenterと独自イベントを使い、Service(Skills)にObserver(イベントリスナー)を利用した例だ。

MVC003.jpg

この構造がMVCモデルを使ってアプリを実装する際の基本パターンである。

MVCがPhysical内に閉じ込められているのに驚いたかもしれない。
でもViewControllerは本来Viewとセットであり、アプリのBrainが他にある以上、「ViewControllerはViewと関連データを従えて、1つのオブジェクトとして振舞う」と考えた方が全体がシンプルになる。この場合ViewControllerはPhysical内にできた「BSPの入れ子」のBrain要素である。「BSPの入れ子」内のService要素にはDataManagerやViewController内のメソッドが相当する。ViewやDataは相変わらずPhysical要素だ。

どうだろうか?MVCが実は「2種類のデータ」を「1つのオブジェクト」としてまとめるための技術だと言われた感想は?実際にコーディングしてる時の感覚とズレるのではないだろうか?

自分もかつてMVCはアプリケーションのメインストリーム(主幹)を占めていると思っていたが、その考えが当てはまるのは、画面数が少ないソフトウェア、もしくはユーザとのインタラクションが主体のソフトウェアに限られる。もしかしたら、その手のソフトばかりを手がけているのかもしれない。

あるいは単にコーディング作業が大変なので、そう思い込んでいただけかもしれない。
でもMVCのControllerの難しさは、それがメインのBrain要素である事から来るのではなく、「クラス」の継承や仮想関数の仕組みでは裏に隠されていたBrain要素を、自分でハンドリングしなければならない事ことから来ている。クラス・インスタンスの自動処理よりViewControllerの自動処理の方が自由度が高いし、できる事も高度だが、その分プロセスを学んだり実装したりが大変なのだ。

いずれにしても複雑な心境だと思うが、Physicalをまとめ上げるBrain役も大役だから頑張って実装しよう。ただし、それがPhysical内で閉じていて、アプリケーションのメインのタスクと違う事は意識しておこう。


MVCモデルを使わないケース
web系やらGUI系のフレームワークでは今や常識と言えるMVCモデルだが、これらを使わないと言う選択もあり得る。それはグラフィックが中心だったり、Viewの再利用率が低い場合などだ。例えばゲームやビジュアルアートでは表示用データが中心でModelとViewを分ける意義が低い上に、表示部品の使い回しが少ないのでViewとViewControllerを分ける意義も低い。

事実、FlashのActionScript3.0ではDisplayObjectの派生クラスが「ViewとViewController」の両方の役割を担っている。DisplayObjectの派生クラスにも独自の変数やメソッドを追加できるのでModel役やサービス提供役を兼ねることができる。つまりやろうと思えば1つのクラスでMVCの全要素(Physicalそのもの)を表現できる。これはゲームやビジュアルアートに使われるActionScriptらしい仕様である。MVCを分ける大義名分として「異なるプラットフォーム間での移植性を高めるため」というのがあるが、そもそもFlashはほとんどのプラットフォーム上で動くからそれを気にする必要もない。

最近話題のUnityというゲームエンジンもMVCモデルではない。グラフィック・オブジェクト中心に全ての仕組みが回っているが、そのグラフィック・オブジェクトの再利用には「Prefab」という独自の機構を使う。

将来的にはこうしたグラフィック中心の流れがメインストリームになる可能性もある。単一オブジェクトの表示に複数のグラフィックを使い分けることも多いからMVCの考え方が全く無くなることはないだろうが、上級者しか知らなくなる可能性はある。

なお、BSPについても規模が小さいうちは無視できる。もちろん規模が大きくなれば、モデルを分けたりBSPを考慮したりする必要が出てくる。

分散システム上のMVC
さて次にwebフレームワーク上のMVCについて触れておこう。
webの場合は技術が複雑に絡んでおり、私自身も正確に把握していない部分が多いので、厳密な定義がやりにくいのだが、イメージだけでも掴んでおこう。

MVC004.jpg

webシステムの最大の特徴はクライアントのwebブラウザとサーバサイドで処理が分かれている点にある。
ブラウザ側で面白いのは内部構造が表示用と永続用で分かれていない様に見える点だ。厳密にはViewは「ブラウザやシステム標準のUI」及び「CSS」を使って表現され、ブラウザがそれをコントロールしている。が、JavaScriptやCSSなどの各処理系からは、ブラウザ上のデータはDOMを通して同列に扱われており、MVCをプログラマーが意識する事はない。

サーバサイドにはそもそもViewが存在しないので表示用に分けようがない。ただしシステム全体で見るとブザウザ側にView、サーバ側にModelとControllerを持ったMVCモデルとなる。サーバ側のControllerはブラウザに転送するデータを制御しており、プロセス管理としては主にページ遷移、イベント処理としてはHTTPコマンドに対するレスポンスを処理する。

今度はBSP視点で見ていこう。
BSPモデルではクライアント側をPhysical、サーバサイドのインフラをBrain、サーバをServiceと大きく捉えることができる。また、Webアプリの場合はクライアントだけでなくサーバ側も入れ子になる。先に述べた様にサーバ側にはMとCがあるので、それがPhysicalとBrainとして入れ子を構成するのだ。これでMVCとBSPの対応は一通り取れた。しかしながら、実際には図上のBSPの体裁が整ってなくて、見れば見るほど疑問が涌いてくる。

BSP擬人化原則
ここで一つの原則を設けよう。
原則としてBSPを入れ子にする時には、必ず中にBSPの3つが揃っている事とする。
私はシステムを俯瞰する際にこの原則から外れている事例をまだ知らない。だから今までの話に出てこなかった要素でも混同等の何らかの形で存在しているはずである。今度の図はこの原則に基づいてBSPの各要素を割振ってある。

MVC005.jpg

BSPは人の活動の基本要素であり、人のメタファだからBSPが3つ揃うと擬人化ができるようになる。擬人化は理解を助ける効果がある。脳内のミラーニューロンが共鳴するからとか、そんな理由らしいが、とにかく「Skillsだったらサービス役、Physicalだったら記録役/窓口役」という感じで擬人化しながらBSPを割振ってみよう。MVCモデルだけで捉えていた時には見えなかったことがたくさん見えて来るはずだ。
先の原則は擬人化のための原則だから「BSP擬人化原則」と命名しよう。

「BSP擬人化原則」は色んなところで、隠れているBrain要素を見つけ出す手がかりになるはずだ。また、MVCと組み合わせればMVペアを見つけ出す役にも立つだろう。何より各ブロックの役割がイメージしやすくなるはずだ。

ところで先ほどの図だが、かなり厳密になったし、これまで見てなかった要素が見えてきて興味深い。が、いささか情報過多な気がしないでもない。意識しなくて良いことは図に描かないという割切りも大事だから、「原則」は必要に応じて適用するといいだろう。


MVCからMVCSへ
ところで「BSP擬人化原則」とMVCを組合わせて使うには一つ問題がある。それはビジネスロジックの扱いが中途半端である点だ。これまでにも述べた通り、ビジネスロジック=サービス要素には汎用に使えるものもあり、SOAに代表される様にモデルから独立して扱われる様になって来ている。つまりModelが変質してきている。その現状を踏まえるとMVCにServiceを加えて「MVCS」と表記した方がシックリ来る。それにMVCSであれば「Model・View≒Physical、Controller≒Brain、Service≒Skills」とBSPとの対応も取りやすい。

これらを踏まえて「MVCSとBSPの基本パターン図」を作ると以下の様になる。

MVC006.jpg

太枠が「MVCS」だ。だいぶイメージしやすくなったと思うが、どうだろう?

MVCSとBSPの使い分け
さて、これまでMVCを様々な視点から見てきた。特にBSPとの比較が多かったがいかがだっただろうか?
MVCを拡張したMVCSであればBSPとの互換性もあるが、両者にはそれぞれ一長一短があり、使い分けが必要である。
MVCSの長所は実装に即している事が多いことだろう。特にMVCアーキテクチャのフレームワークを使っているならそのまんまだ。また、MとVが分かれているのでインプットとアウトプットの処理が大きく違う場合には挙動を把握しやすい。
BSPの良さは何といっても擬人化で考えられる点だ。また、入れ子の原則がハッキリしているので複雑なシステムを分解するのにも向いている。また、表示等の詳細に惑わされず、本質的なデータのみについて考えることにも向いている。

Physicalというメタファの意義
Physicalでデータと表示を混同していることにMVCに慣れている人はかなり違和感を覚えるだろう。だが、先のActionScriptの様に1つのクラスで両方実現できる例もあるし、webブラウザの様に「コンテンツとDOM」を中心に別々の技術を組合わせてMVCを上手く隠蔽している例もある。そこにはMとVの2重持ちと言うイメージはない。

こうした場合、データこそが本質であり、それに特有のサービスやら見た目やら制御が加わるとBSP群としてのPhysicalが形成される。この一塊のPhysicalはクラス・インスタンスにそっくりだ。また前々回のユースケースでも取り上げた様に「アクター」と呼ぶこともできるし、独立した「システム」と見る事もできる。自律性が高ければ「エージェント」と呼ぶこともできるだろう。MとVを分けて考えていてはこうした広がりが見えてこない。
まさにPhysicalならではの面白さだ。

編集ツールとMVCの隠蔽
最後にちょっとだけ「そもそも」話をしよう。
そもそもMVCの「MとC」を統合しようなどと私が考え始めたのは、立続けに編集ツールばかりを作ってきたからだ。
利用者のリクエストに応える普通のアプリなら、インプットとアウトプットは大きく違っているのが普通だ。そうした状況では「MとCの統合」などという発想は浮かばないだろうが、編集ツールだとインプットとアウトプットはほとんど同じである。当然ながら、入力した内容を確認のために画面に描き戻す処理が頻発する。これをいくつもの言語、いくつものアプリで続けてやれば、さすがに嫌になる。言ってみればこれは定型処理なわけで、隠蔽するのがセオリーだろう。MVCの枠組みで考えていたらこの隠蔽は不可能だが、あいにくOS標準のAPIもMVCフレームワークとして提供されている。だったらMVCごと隠蔽すれば良い。

Physicalはインタフェース役となり、ユーザの操作情報をイベントを通じてBrain要素に上げる。Brainは操作内容に応じたSkills(Service)を使ってPhysical(データ)を編集する。Physical(データ)は自動的に表示に反映される。これはwebブラウザの挙動そっくりだ。

もちろん、こうしたことは他の種類のアプリではあまりない。だからMVCSを使い続けるという選択肢もアリだ。ただしBSPには擬人化などMVCの隠蔽以外のメリットもあるので、それは試して見てほしい。

さて、今回は随分長くなったがこれでおしまい。

次回からはイベントシステム等の既存のプログラミング言語で広く使われている技術について扱おう。
具体的には各技術で行われている「自動処理の隠蔽」について考察する。
つまり「どれだけ頭脳を隠しているか調べよう」って訳だ。
これらを理解する事は基礎固めにはなるし、使った事がないプログラミング言語を理解する際の道標になってくる。

そんなわけで次回もお楽しみに。

nice!(0)  コメント(0)  トラックバック(1) 
共通テーマ:パソコン・インターネット

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 1

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。