May 09, 2017

結局、MVC とは何なのか?

この記事は IT エンジニア向けです。

もはや 2 周くらい回って、そろそろ「ダサい」という人も出てきそうな MVC という言葉。 まあ、技術は日々進歩するわけだし、色々な意見があるだろう。 俺自身、MVC を使う事を推奨するつもりは毛頭ない。 (非推奨という分けでも無いが。)

でも、少なくとも MVC は一時代を築いた事があり、現在でも無視できない考え方だ。

何で MVC という考え方が生まれたのか? その根本思想は何なのか?

個人的には MVC は構造化プログラミングの一つの良い例であり、GUI 以外にも色々と応用が利く考え方だと思っている。

実際、俺は今、業務で書いているプログラムの全体設計に MVC を参考にした。 GUI ではないので、当然 MVC の出番はない。 でも、俺の作ったプログラムには models というディレクトリが存在する。

なので、今回は MVC についての俺なりの理解をザックリと書いてみる。 なお、個人の意見、解釈が満載なので、ご了承ください。 (と、予防線を張っておく)

さて、まず最初に MVC のそれぞれの単語について、簡単に考えてみよう。

View とは何だろうか?

View には「景色」「外見」などの意味がある。 その名の通り、View とは人間への見た目を司る部分だ。 Rails だったら、template 部分にあたるだろう。

次に、Controller とは何だろうか?

Controller とは「何かを制御する物」という意味だ。 では、MVC の Controller は「何を」制御するのだろうか? そして、MVC において Controller は何をするのだろう?

最後に、Model とは何だろうか?

そもそも、Model という言葉の意味は何だろう? 「人体模型」、「ファッションモデル」 これらは全て Model な分けだが、本来、どういう意味だったのだろう? 一体全体、MVC の Model とは何なのだろう?

View は良いのだが、Controller と Model は直観的とは言い難い。 少なくとも、俺が初めて MVC (Rails) を学んだ時は、良く分からなかった。

ただ、今では View 以外の部分を Controller と Model に分けた事が MVC の凄さの源だと思っている。 なので、それぞれを簡単に紹介していく。

最初に、Model という言葉の意味からだ。

俺は、「A の Model」とは、 「A の重要な、注目している特徴をと同じ特徴持っている物」 という意味だと思っている。

多くは、A 自身を直接扱うのが困難な時に、代替として使うだろう。

「人体模型」は人体と同じ「臓器の形、大きさ、場所」等の特徴を持っている。 人体の臓器の勉強をする際に本物の人体を使うのが難しいので、 同じ特徴を持つ人体模型を使うのだ。

同様に、「ファッションモデル」は、その衣装を着る人と同じ 「性別、年齢」等の特徴を持っているのだろう。

では、プログラムにおける Model とはどういう意味だろう?

これは、「そのアプリケーションの特徴を持つ物」という意味だ。

例えば Users Model では

  • 各ユーザーは email address を 1 個持つ この email address は全体で重複しない

  • 各ユーザーはニックネームを持つ。 このニックネームは登録時に作成され、以後変更は出来ない

等、アプリケーションのユーザーに関する特徴を実装している事だろう。

さらに、MVC の Model には 1 個、大きな制約がある。

それは、 「アプリケーションの同じ特徴を、複数の Model に記載してはいけない」 という事だ。

例えば、「各ユーザーは email address を 1 個持つ」というアプリケーションの特徴を Users Model に書いた場合、 「MailAddresses Model」に同じ特徴を書いてはいけない。

これを許可してしまうと、

  • MailAddresses Model と Users Model で結果が変わる

  • MailAddress Model を使うと、Users Model の「各ユーザーはニックネームを持つ」という特徴が消えてしまう

等のバグが生まれる可能性が増えてしまう。

この制限を加えるとで、同じ処理を行うために必要な Model の組み合わせを 1 通りに限定し、プログラムの見通しを良くしている。

「重複を許さない」という意味では DRY (Don't Repeat Yourself) と似ているかもしれない。

でも、通常 DRY が「コピペしない、同じ処理を 2 箇所に書かない」だけだが、Model の制限は「異なる処理であっても、アプリケーションの同一の特徴に関わる物は異なる Model に書いてはいけない」という意味で、より制限が強いと言える。

要するに、異なる処理でも関連していれば 1 個の Model にまとめる必要が出てきたりするわけだ。

一方、Model のこの制限には、悪い事もある。 大きな Model を作ろうとすると、他の Model と特徴が被ってしまう事が多くなる。 必然的に、各 Model は小さくなるだろう。

Model が小さく、疎結合に分かれているのは良い事だ。 でも、それをまとめる大きな Model が作れないのは嬉しくない。 1 個のアクションで多くの Model を操作する必要が出てくるからだ。

ちょっと Users の事は忘れて、メモ帳や Word のようなエディタを開発する事を考えてみよう。

このエディタで「終了」ボタンが押された時のフローはどうなるだろう? 例えば、こんな風になるだろう。

  • 現在開いているファイルの最新状態が保存されているか確認

  • もし、最新状態が保存されていないならば、ポップアップを出してユーザーに確認

  • ユーザーが「保存する」を選択したら、保存処理を行う

  • プログラムを終了する

さて、この一連の処理を全て Terminate Model に記載するべきだろうか? そうすれば、View からはその関数を呼び出すだけで済む。

(補足:Rails では View から Model を呼び出せないが、旧来の MVC では許される)

いや、それは現実的ではない。

最低でも、保存処理は Terminate Model から分けるべきだろう。 なぜなら、保存処理を Terminate Model に書くと、保存に関する特徴のいくつかは Terminate Model に紐づいてしまう。 (例えば、「デフォルトの拡張子は ".txt" にする」など) すると、他の Model に保存処理を書けなくなってしまう。

「終了せずにデータの保存だけする」という場合に Terminate Model を呼び出すのは、あまりにも筋が悪い。 全ての処理を Terminate Model に書くことはあきらめよう。

では、if 文のネストと適切な Model の呼び出しを全て View に書くか? それも良くない。

終了ボタンが複数個所にある場合、全ての場所にコピペするのだろうか? また、ボタン毎にこれだけの処理を記載していたら View が肥大化してしまう。

そこで、Controller の出番だ。

Controller は一連の処理で必要な Model の呼び出しと、その制御フローを記載する。 上記の例では、Terminate Controller を作って、その中で if 文や各 Model の呼び出しを行えばよい。

View からは Terminate Controller だけを呼び出せば良い。 Controller が制御するのは、Model だったのだ。

誤解を恐れずに言うのであれば、Controller とは「『同じ特徴を複数の Model に記載してはいけない』という制限を取り除いた Model」という表現も間違ってはいない。

ただ、Model の制限は、プログラムを整理してより良くするために取り入れたもの。

「Controller は制限が無い、Model のスーパーセット」 というより、 「Model は自動整理機能つき。どうしても Model に書けない時だけ Controller に逃げる。ただし、その場合はスパゲッティにならぬよう自分で気を付ける必要がある」

と考えた方が良いだろう。

必然的に Controller からは複雑なロジックは減り、Model 呼び出しの為の簡単なマクロのような物になるはずだ。

「ロジックを Controller から Model に移せ」という格言にも通じる。

Model とはアプリケーション固有のロジックを記載したライブラリ。 Controller とは Model をユーザーフレンドリーにするためのラッパー、ショートカット(ユーザーとは View を書く人)

と考えても良いだろう。

(アプリケーション固有ではないロジックは、Model ではなく汎用ライブラリとしてパッケージ化し、他のアプリでも共有しよう。)

話が長くなったので一旦、話を整理しよう。

MVC において、

  • Model とはアプリケーションの特徴(ロジック)を重複なく記述した部分

  • View とは GUI の見た目に関わる部分

  • Controller とは、Model を呼び出す制御 (条件分岐、ループなど) をする部分

となる。

今回の記事とは関係ないのだが、誤解を生むと嫌なので一応補足しておく

Controller は Model 「だけ」を呼び出す物では無い。 Controller から別の Controller を呼び出す事は超 OK。

Model が別の Model を呼び出す(又は、has-a の形で内包する)のは、ライブラリやフレームワークの思想に合わせよう。

これを許すと、実質的に一つの特徴を複数の Model が共有する事になる。 つまり、Model 同士が密結合になる。 個人的には好きではない。 だが、一般的には許される事が多いようだ。 フレームワークの思想に一人で反逆しても意味が有る事は少ない。 必要に応じて素直に従おう。

ところで、Rails のような Web アプリでは Model というと RDB へのアクセスを思い浮かべる人も多いだろう。

これは、「アプリケーションの特徴は、RDB のテーブル構造だけで記載出来る事も多い」という事に気が付いた DHH (Rails を作った人) が、フレームワークに専用の仕組みを作った事に由来する。

俺はコンピューターの歴史に詳しいわけではないが、 GUI を作るうえで「View とそれ以外の部分に分ける」 という考え方は、MVC 以前から存在していたらしい。

MVC の発明とは、View 以外の部分をさらに Model と Controller に分けた事。 Rails の発明とは Web アプリを作る際に MVC から Model の考えを拝借した事、および Model 機能の多くを RDB のテーブル設計で補う考えをフレームワークに明示的に取り入れた事。

そんな風に思っている。

さて、ここまでも個人的な解釈が満載なのだが、ここから下は超個人的な意見である。 注意してほしい。

ここまで MVC の Model と Controller について長く語ってきた。 この考え方は GUI にのみ通じる物だろうか?

そんな事は無い。 プログラムには多かれ少なかれロジックは存在する。 ならば、ロジックを Model と Controller に分けるという考え方は ロジックの存在する(つまり全ての)プログラムで応用可能だ。

さらに言うと、Rails の「全てをプログラムに頼らず、RDB のテーブル設計で解決する」という発想も個人的には大好きだ。

より汎用的に言えば、RDB に限らず 「アプリケーションの特徴の一部をミドルウェアやサーバー構成、ネットワークに反映させる」 という考えをすると面白い。

例えば、 「冗長化のために Web サーバーを複数台構築する」 という考え方は間違っていないが、少し物足りない。

「このアプリケーションは、複数人で同時に使える特徴を持っているべき。ユーザーの使用感は他のユーザーの使用状況に関係ないという特徴を持っているべき。ユーザーのリクエストが何かの拍子に失敗しても、再実行すれば成功するべき」 「Web サーバーが複数台存在して、各 Web サーバーが勝手にリクエストを処理するというアーキテクチャは、この特徴とマッチしている」

と考えられないだろうか?

つまり、全体のアーキテクチャを考える時、

「仕様や処理」を軸にするのではなく、

「アプリケーションが持つべき特徴は何だろう?」 「その特徴にマッチする構成は何だろう?」

という風に考えてみるのだ。

すると、「頭の中で描いた処理がそもそも良くなかった」と気付く事もあるだろう。 「仕様がおかしいのではないか?」と疑問がわくかもしれない。

また、個々の処理はプログラムの改修等で簡単に変わってしまう。 根本方針をちゃんと考えたならば、「アプリケーションの特徴」は個々の実装よりも寿命が長くなるだろう。 アーキテクチャというプログラムより寿命の長い物を考える時に、、同様にプログラムより寿命の長い「特徴」を参考にできれば幸せになれるのではないか?

「言葉遊びに過ぎない。結局、考える事は同じだよ」と言う人もいるかもしれない。 確かに、それは本当。 でも、アプリケーションの特徴は要件と直結している。 要するに、「仕様」よりも「特徴」という言葉の方が、より抽象化された上位概念なのだ。

作業を進めていくと、どうしても「木を見て森を見ず」の状態になりやすい。 すると、そもそも論で論破されるような、つまらない過ちを犯してしまう。

そんな事を防ぐために、時には必要以上に上位レイヤーで考えるのも良いものだ。 言葉遊びと笑われようが、これは良いきっかけになる。

以上。 最後は少し乱暴というか、個人的な意見を述べ過ぎてしまったが、MVC の概要でした。