歴史から見るGUI MVCとWeb MVC
本記事では、Trygve Reenskaug氏による「The Model-View-Controller (MVC): Its Past and Present」(2003年)を主な参考資料として執筆しています。記事内の解釈や説明は、筆者自身の意見を交えたものであり、元資料の内容を再構成したものです。
この記事では、Trygve Reenskaug氏による「The Model-View-Controller (MVC): Its Past and Present」を引用しています。以下は、引用のための許可条件に基づき記載します。
This presentation is copyright ©2003 Trygve Reenskaug, Oslo, Norway. All rights reserved.
Permission to make digital or hard copies of part or all of this work for personal or classroom use is granted without
fee provided that the copies are not made for profit or commercial advantage and that copies bear this notice and full
citation on the first page.
引用元:
著者: Trygve Reenskaug
タイトル: "The Model-View-Controller (MVC): Its Past and Present"
発行年: 2003年
URL: http://heim.ifi.uio.no/~trygver/mvc/index.html
はじめに
MVCパターンは古くからありますが、現在でも、Fluxの文脈でも言及されています。
MVCについて調べると、MVCのそれぞれの責務について言及する記事は多いですが、そもそも「MVCとは何か」という部分について曖昧さを感じることがありました。
そこで、MVCについて、歴史を踏まえて改めて調べ、GUIアプリケーションでのMVC(GUI MVC)とWebアプリケーションでのMVC(Web MVC)の違いを整理することで、MVCの本質について深掘りしてみました。
GUI MVC と Web MVC
現在、「MVC」と言えばWeb MVCを指すことが多いですが、MVCには2つの種類があります。GUI MVC(Smalltalk MVC)と、後に誕生したWeb MVCです。
GUI MVCはSmalltalk MVCとも言われ、1978年に、トリグヴェ・リーンスカウク氏が考案したとされています[1]。
この2つのMVCの大きな違いは、ステートフルかステートレスかという点にあります。
GUI MVCはModelがステートフルな設計であるのに対し、Web MVCはHTTPプロトコルの特性上、ステートレスな設計が基本です(詳細は後述)。
まずはGUI MVCが誕生した背景と流れを見ていきます。
Smalltalkとは
クラスベースのオブジェクト指向プログラミング言語です。
MVC誕生背景
MVCが登場した背景には、DynaBook構想が関係しています。
Dynabook構想では、「誰でも(子供から大人まで)簡単にプログラミングできるコンピュータを作ろう」という目標を掲げており、その実現を目指す過程でGUI MVCパターンが誕生しました。
システムは、エンドユーザーが理解できるシンプルで均一なルール(メッセージング)と要素(オブジェクト)で構成され、このシステム自体をもユーザーが自由な発想で再定義できる柔軟性や可塑性を持ち合わせていることも肝要である。
ー 「ダイナブック」『フリー百科事典 ウィキペディア日本語版』。2024年11月12日 (火) 22:32 (日本時間)
DynaBook(ダイナブック)とは
アラン・ケイが提唱した、理想のパーソナルコンピュータです。誰でもプログラミングできるという条件を満たすOSは、まだ存在しません[2]。
GUI MVC
MVCの最初の考案は、エンドユーザーの「メンタルモデル」をModelに分離することから始まりました。
そして、この「メンタルモデル」がMVCの本質、核の部分です。
MVCは人間とそのメンタルモデルに関するものであり、オブザーバパターンに関するものではない。
ー DCIアーキテクチャ - Trygve Reenskaug and James O. Coplien
The essential purpose of MVC is to bridge the gap between the human user's mental model and the digital model that exists in the computer.
MVCの本質的な目的は、人間であるユーザーのメンタルモデルとコンピューター内に存在するデジタルモデルの間のギャップを埋めることである。
ー MVC XEROX PARC 1978-79
メンタルモデルとは?
メンタルモデル(英: mental model)とは、頭の中にある「ああなったらこうなる」といった「行動のイメージ」を表現したものである。
ー 「メンタルモデル」『フリー百科事典 ウィキペディア日本語版』。2024年5月30日 (木) 14:55(日本時間)
例えば、エレベーターに対して私たちは「ボタンを押せばエレベーターが動き、行きたい階に連れて行ってくれる」と思っています。
でも実際には、「エレベータがどうやって動いているのか」を詳しく知る必要はありません。
なぜなら、私たちの頭の中には、経験や学習によって作られた「何をしたらどうなるか」という簡単なルールがあるからです。この、頭の中にあるルールを、メンタルモデルと呼びます。
コンピュータにおけるメンタルモデル
エンドユーザーが「システムはこう動くだろう」と期待する動作や構造のことです。
具体的には、データと、その振る舞い(ビジネスロジック)が該当します。
メンタルモデルと複雑性
また、メンタルモデルを具体化すると、それは複雑なものとなります。
They’re tools for compressing complexity into manageable chunks.
メンタルモデルは、複雑なものを扱いやすい塊に圧縮するためのツールです。
ーMental Models: The Best Way to Make Intelligent Decisions (~100 Models Explained)
メンタルモデルの分離
Reenskaug氏は、エンドユーザーの「メンタルモデル」をModelというコンポーネントに分離しました。
The Model holds the user’s object model with its information and behaviour, reflecting the user’s mental model.
Modelとは、ユーザーのオブジェクトモデルを保持し、その情報と振る舞いを含むことで、ユーザーのメンタルモデルを反映している。
ー The Model-View-Controller (MVC): Its Past and Present
UIとメンタルモデル
UIはメンタルモデルではありません。
UIは、メンタルモデルを形成、サポートするものです。
例えば以下のようなUIでは、カーソルを合わせると色が変化したり、ポインタが変化します。この視覚的なフィードバックにより、「クリックできるもの」とユーザが認識します。
この時、「クリックすると」というメンタルモデルが形成されます。
しかし、「クリック」した時に内部で走るイベントは、処理の詳細であり、UIではありません。
処理の詳細は、ユーザに見えないものであり、この見えないものに対するイメージがメンタルモデルのため、UIはメンタルモデルではないのです。
Modelとメンタルモデル
メンタルモデルとは、先述した通り「何をしたらどうなるか」というイメージです。
しかし、Modelの責務はデータとその振る舞いであり、メンタルモデルの「こうなる」の部分に該当するため、厳密にはメンタルモデルの一部です。
では、なぜModelはメンタルモデルを表していると言えるのか?
これは、ユーザがあたかもModelを直接操作しているという、錯覚(ユーザーイリュージョン)が生じるためです。
The ideal MVC solution supports the user illusion of seeing and manipulating the domain information directly.
理想的なMVCソリューションは、ユーザがドメイン情報を直接表示および操作しているというユーザーイリュージョンを実現するものである。
つまり、MVCとはユーザーがModel(ドメイン情報)を直接操作しているように感じさせる仕組みと言えます。
実際には、ユーザーがクリックなどの操作を行うとイベントが発生し、そのイベントをControllerが処理してModelが実行されます。しかし、ユーザーはこの内部の流れを意識せず、直感的に操作できるようになっています。
ここで、MVCのModelをメンタルモデルの「ああするとこうなる」に当てはめると、
「ModelのAPIを操作すると、Modelのビジネスロジックが実行される」となります。
したがって、Modelはユーザのメンタルモデルを表していると言えます。
ユーザーイリュージョン
ユーザー・イリュージョンは、informationとexformationの2つの要素から構成される。
ー 君はインターフェイスではない方のUIを知っているか
“Exformation” means something that is intentionally omitted from the information.
「Exformation」とは、情報から意図的に省略されたものを意味する。
ー User Illusion; the root of User Interface, and Information vs. Exformation.
MVCにおけるユーザイリュージョンとは、UIという可視化された情報(infomation)から、可視化されていない情報(exformation)を操作しているという錯覚を生み出すことです。
しかし、実際にエンドユーザは、ドメイン情報(Model)を直接操作することはありません。
Controller、Viewというツールを通じて、ドメイン情報を操作しています。
UIは作業場というメタファーであり、その作業場で、ドメイン情報を直接操作しているように錯覚させることで、ユーザは作業に集中することができます。ユーザは、ControllerやViewのイベントなどを気にする必要はありません。
さらに、ユーザーイリュージョンを効果的に生み出すためには、メンタルモデルとUIが分離している必要があります。この理由は、変更にあります。
変更
ユーザの想定しているUIと開発者が作ったUIが異なる場合、ユーザに形成されるメンタルモデルと、開発者が想定するメンタルモデルがずれてしまう可能性があります。
このような場合でも、メンタルモデルとUIを分離しておけば、UIだけを変更したり、逆にModelだけを変更したりして、差異を解消しやすくなります。結果として、ユーザと開発者の間でメンタルモデルが食い違っている場面でも柔軟に対応でき、システムの拡張性や保守性が向上します。
There is no guarantee that the domain services correspond to the mental models of the different users. If they do, there is no problem. If they do not, we need to bridge the gap.
異なるユーザたちのメンタルモデルにドメインサービスが対応しているという保証はない。対応していれば問題ないが、対応していない場合は、そのギャップを埋める必要がある。
ー The Model-View-Controller (MVC): Its Past and Present
ModelとAPI
Modelはメンタルモデルを表すため、目的に沿ったAPIのみを公開すべきと考えられます。
例えば、ToDoアプリで「[完了]ボタンを押すとタスクが完了する」という動作を実現する場合、タスクを完了する処理はCompleteTask()
に統一し、内部の詳細処理(MarkAsCompleted()
やSortTasks()
)は非公開にする設計にするべきです。これにより、メンタルモデルで期待される「[完了]ボタンでタスクが完了する」という直感的な動作をAPIとして提供できます。
したがって、Modelが公開するAPIは、目的(メンタルモデルの「こうなる」の部分)に限定するべきです。また、メソッド名も目的を的確に表現したものとするのが適切です。
// Controller
public MarkTaskAsCompleted(string taskId) {
_taskModel.CompleteTask(taskId);
}
// Model
public CompleteTask(string taskId) {
MarkAsCompleted(taskId);
SortTasks();
}
Controllerのメソッド名について
例示したクラス図のControllerのメソッド名をMarkTaskAsCompleted()
としていますが、これは、ユーザのアクションや操作を反映しています。
これは、Controllerがユーザからの入力を制御する責務を持っているためです。
Controllerの責務については、入出力の分離(EditorをControllerとViewに分離)で書いています。
EditorとModelの分離
Reenskaug氏は、エンドユーザのメンタルモデルをModelと定義し、それ以外をEditorと定義しました[3]。
Editorはツールであり、ユーザがModel(メンタルモデル)とやり取りするための仲介役として設計されています。そして、このツールが、ユーザイリュージョンを実現するためのツールです。
To give the user a Tool for performing one or more tasks.
The Tool shall give the user an illusion of interacting
directly with the model.
ユーザーに1つ以上のタスクを実行するためのツールを提供する。
そのツールの責務は、ユーザーに対してModelと直接やり取りしているかのような錯覚を与えることだ。
ー The Model-View-Controller (MVC): Its Past and Present
それぞれの具体的な責務は以下の通りです。
Modelの責務
-
メンタルモデル
- データの管理
- 振る舞い(ビジネスロジック)
Models represent knowledge.
モデルは知識を表す。
ー MODELS - VIEWS - CONTROLLERS
Editorの責務
-
Modelとユーザの仲介役
- ユーザが直接Modelに触れるのではなく、エディターを通じてModelとやり取りをする。
-
表示に関すること
- Modelのデータを、表示用に整形すること。
- UIを構築すること。
- UIを出力(描画)すること。
-
操作の提供
- UIから入力を受け付けること。
- 例)ボタンのクリックや、キーボード入力など。
ModelとEditorのやり取りの流れ
ModelとEditorのやり取りの流れ
※実線の矢印は送信、破線の矢印は応答として使用しています。
このModelとEditorの分離によって、要件の変更に柔軟に対応できるようになりました。
特に重要な点は、「ModelがEditorに依存しない」設計(疎結合)になったという点です。
この分離により、UIを変更しても、Modelに影響を与えなくなりました。
Facadeパターン
The Model can be implemented as a Facade
モデルはファサードとして実装することができる
ー The Model-View-Controller (MVC): Its Past and Present
ファサードパターンの使用によって、Modelが持つ複雑なビジネスロジックを隠蔽し、ModelとEditorのさらなる疎結合を実現します。
Facadeパターンを用いるメリット
- 要件の追加があった際には、既存のModelを変更することなく、新たなModelを追加するだけで済む(疎結合)。
- 要件の変更があった場合は、対象のModelのみを変更するだけで済む(疎結合)。
- FacadeでModelのAPIを一元管理することで、全体の操作内容を把握しやすくなる(シンプルなAPIの提供)。
Facadeパターンを用いた、ModelとEditorのやり取りの流れ
入出力の分離(EditorをControllerとViewに分離)
The input and output aspects of the Editor are technically very different with few interdependencies.[…]a View object responsible for presentation, and a Controller object responsible for taking and interpreting input from the user.
Editorの入力側(input)と出力側(output)は技術的に非常に異なるものである。[…] 1つはプレゼンテーションを担当するビューオブジェクト、もう1つはユーザーからの入力を受け取り解釈するコントローラーオブジェクトである。
ー The Model-View-Controller (MVC): Its Past and Present
Editorは仲介役であると説明しましたが、仲介役には、以下の2種類があります。
2種類の仲介
- ユーザからの入力を受け取り、Modelに渡す。
- Modelからの結果を受け取り、描画(UIの出力)する。
Reenskaug氏は、Editorの入力と出力を分離させました[4]。
-
Controller(入力)
- ユーザーからの入力を受け取り、それを解釈してModelに渡します。
-
View(出力)
- 表示に関すること(プレゼンテーションロジック)を担当。
Model-View-Controllerの処理の流れ
ControllerとViewの依存関係
上記の図では、分かりやすくするためにControllerとViewは完全に分離して書いていますが、実際にはControllerとViewは、互いに依存し、不可分な関係にあります。
例えば、ただダイアログを表示するなど、ビジネスロジックを必要としないUI操作があります。なので、この依存は、Controller → Viewになります。
Model-View-Controllerの処理の流れ
また、ControllerはUI(View)で発火したイベントによって処理が開始されます。
なので正確には、処理の流れは、ユーザ → Controllerではなく、View → Controllerの流れになります。
Conversely, a view should never know about user input, such as mouse operations and
keystrokes. It should always be possible to write a method in a controller that sends messages
to views which exactly reproduce any sequence of user commands.
Viewはユーザーからの入力(例えばマウス操作やキーボード入力)について深く知るべきではない。コントローラー内でメソッドを記述し、そのメソッドがビューにメッセージを送信することで、任意のユーザーコマンドの処理を正確に再現できるようにするべきである。
ー MODELS - VIEWS - CONTROLLERS
Model-View-Controllerの処理の流れ
ModelとViewの依存関係
処理の流れは、Modelが変更されたら、Viewに通知します。Viewは通知を受け取ると、Modelの状態を取得して、UIを更新します。
以上が、MVCが生まれた背景です。
GUI MVCのクラス図
ModelとViewの依存関係
最後に、GUI MVCの依存関係をクラス図で見ていきます。
ModelとEditorのやり取りの流れで「ModelがEditorに依存しない」設計が重要であると述べました。しかし、GUI MVCでは、「Model → View」の処理の流れとなっているため、ModelがViewに依存しているように見えます。
しかし、実際にはModelはViewに依存せず、ViewがModelに依存しています。
これを実現しているのがObserverパターンと依存性逆転の原則(DIP)です。
ObserverパターンとMVC
-
Observerパターン
- Observerパターンの登場人物は、Observer(通知を受け取る側)とSubject(通知を送る側)です。
-
Model
- Modelは通知を送る側(Subject)です。Modelは、View(Observer)に通知を送るだけなので、Viewの詳細を知っている必要がありません。
-
View
- Viewは通知を受け取る側(Observer)です。Model(Subject)から状態の変更を受け取ると、すぐにModelの状態を取得します。ViewはModelの状態を取得(参照)するため、Modelについて詳細に知っている必要があります。
これを踏まえて、クラス図を書くと、以下のようになります。
GUI MVCのクラス図
この図を簡略化すると、MVCでよく見る、以下の図になります。
GUI MVCのクラス図(簡略化)
GUI MVCの特徴 まとめ
ステートフルな設計
今まで見てきたMVCはGUIアプリケーションで使用されるMVCパターンです。
そして、このMVCパターンにおけるModelはステートフルな構造を持っていて、Modelはアプリケーションの状態(データ)を保持し続けます。
ユーザの操作によって、Modelの状態が変更されると、Modelはその変更をViewに通知します(この通知の仕組みがObserverパターンです)。
この通知の仕組みから、GUIアプリケーションのMVCパターンは、状態管理を前提に動作していることがわかります。
したがって、GUIアプリケーションのMVCパターンは「ステートフルな設計」であると言えます。
それぞれの責務
- Model:メンタルモデルをシステムに反映している。ビジネスロジックと、データの状態管理を担当する。
- Controller:入力に対して制御を行い、必要に応じてModelを操作したり、Viewの生成を指示する仲介役
- View:データを表示用に整形し、UIを構築する。また構築したUIの出力。
シーケンス図
シーケンス図でGUI MVCを見ると、以下のようになります。
ビジネスロジックを含まない場合
例として、ダイアログを表示する場合
ビジネスロジックを含む場合
クラス図
GUI MVCのクラス図(簡略化)
Web MVC
ここでは、Web MVC = サーバサイドのMVCとして扱います。
Web MVCとGUI MVCの異なる点は、以下のようになります。
GUI MVC | Web MVC | |
---|---|---|
状態管理 | ステートフル | ステートレス |
入力の受け手 | 直接Viewが受け取る | リクエストを通じてControllerが受け取る |
レンダリング | 直接UIを描画 | サーバーサイドレンダリング |
このような違いは全て、WebアプリケーションがHTTPプロトコルとクライアントサーバモデルを採用していることによるものです。
ステートレスな設計
Web MVCはステートレスな設計となっています。
これは、HTTPプロトコルがステートレスだからです。各リクエストが独立し、サーバ側はリクエストの状態を保持しません。
なので、Modelのインスタンスは通信毎に生成され、GUI MVCのように状態を保持できません。
サーバサイドレンダリング(SSR)
GUI MVCのクラス図で、ViewはUIを構築する際、Modelの状態を取得していることを説明しました。
しかし、Webはクライアントサーバモデルであるため、HTMLはクライアント側にあり、サーバー上のModelには直接アクセスできません。
なので、View(HTML)がModelを参照するためには、サーバサイドでHTMLを構築する必要があります。
ASP.NET Coreを例にすると以下のようにViewはModelを取得して、HTMLを生成しています。
<!-- Views/User/Index.cshtml -->
@model WebApplication.ViewModels.UserViewModel
<!-- 中略 -->
<p>@Model.Name</p>
<p>@Model.Adress</p>
ユーザ → Controller
GUI MVCの処理の流れはユーザ → Viewの順であると説明しました。
しかし、Web MVCでは、Viewからイベントを直接受け取ることはありません。
ViewはModelを参照してHTMLを生成するだけです。
そのため、イベントはリクエストを通じてControllerが受け取ります。
窓口がControllerとなったため、一度Viewを介する必要がなくなりました。そのため、依存関係は以下のようになります。
それぞれの責務
以上の内容を踏まえた上で、責務は以下のようになります。
- Model:メンタルモデルをシステムに反映している。ビジネスロジックと、データの状態管理を担当する。
- Controller:クライアントからのリクエストに対して制御を行い、必要に応じてModelを操作したり、Viewの生成を指示する仲介役。Viewの生成結果をクライアントに返却する。
- View:データを表示用に整形し、UIを構築する(プレゼンテーションロジック)。
以上、GUI MVCとWeb MVCの違いとその歴史を振り返りつつ、MVCの本質に触れてきました。
調べていく中で、MVCの根幹にある「メンタルモデル」という概念が面白かったです。
MVCだけでなくMVVMやFluxを学習する際にも参考になれば幸いです。
参考文献
-
「Model View Controller」『フリー百科事典 ウィキペディア(Wikipedia)』。2021年11月25日 (木) 01:46 (日本時間)https://ja.wikipedia.org/wiki/Model_View_Controller ↩︎
-
「ダイナブック」『フリー百科事典 ウィキペディア(Wikipedia)』。2024年11月12日 (火) 22:32 (日本時間)https://ja.wikipedia.org/wiki/ダイナブック ↩︎
-
The Model-View-Controller (MVC): Its Past and Present、9ページ参照。 ↩︎
-
The Model-View-Controller (MVC): Its Past and Present、10ページ参照。 ↩︎
-
The Model-View-Controller (MVC): Its Past and Presentの、P-7: INPUT/OUTPUT SEPARATION(MVC/1980)の図(10p)のこと。 ↩︎
Discussion