🔍

起源から整理するGUIアーキテクチャパターン

2024/10/28に公開

はじめに

ソフトウェア開発、とくに Web やモバイルアプリなどの GUI アプリケーションの開発において、MVVM や MVC といったアーキテクチャパターンの話題は避けて通れません。

GUI アプリケーションの開発において頻出する課題を「パターン」として捉え、それに対する一般的な解決策に名前をつけることで、ひとつひとつのプロジェクトが 1 から解決方法を模索することなく効率的にその課題に対処できることから、これらのパターンはよく参考にされ、実際のソフトウェア開発で取り入れられます。

しかし、内容的にも状況的にも、これらのパターンはただ「取り入れれば良い」という単純なものではありません。

アーキテクチャパターンに対する認識齟齬

アーキテクチャを策定、適用するにあたって大事なことのひとつに メンバー同士の認識を合わせる ことが挙げられます。

アーキテクチャを策定しただけではまだ「絵に描いた餅」の状態です。それを実現するにはチームメンバー一人ひとりがそのアーキテクチャを同じように理解し、同じルールに則ってコーディングをしなければなりません。

それができない場合、プルリクとして提出されたコードを見ながら認識齟齬を埋めるためのコストが発生し、場合によってはその議論が平行線を辿って時間だけが浪費されてしまうことも考えられます。さらに認識齟齬が解消されないままマージされたコードは、将来「読みづらさ」というコストを発生させることになります。

では一人ひとりがちゃんと勉強すれば認識が合うのかというと、そうでもないように思います。

世の中のアーキテクチャパターンについての記事や議論を見ていると 各パターンに対する認識が人によって異なる問題がある ように感じるためです。

「これが正しい」としつつ具体的な根拠は提示されない、参考にした記事も元々は別の記事を参考に書いただけでその根拠が不明である、言語やフレームワークが異なるためにそもそも議論の前提が揃わない、といった状態は一度ではなく目にします。

起源を拠り所にした交通整理

この記事では、そのような議論を少しでも交通整理すべく、そのパターンが提唱された段階ではどのような内容だったのか を MVC, MVP, MVVM の 3 つのパターンについてまとめてみたいと思います。

ただし、間違ってはいけないのは 「正しさ」を振りかざすためにこの記事があるわけではない ということです。

アーキテクチャはソフトウェア開発における課題を解決するために存在するものであり、その課題はプロジェクトごとに異なります。全く同じ状況、全く同じビジネス、全く同じメンバーで開発されるソフトウェアはひとつとしてありません。

Martin Fowler はこの点について Patterns of Enterprise Application Architecture という書籍の中で アーキテクチャパターンは「生焼け(half baked)」の状態であり、必ずプロジェクトごとに仕上げが必要である と書いています。

I like to say that patterns are “half baked,” meaning that you always have to finish them off in the oven of your own project.

つまり、アーキテクチャパターンを学ぶ上で大事なのは「パターンを正確に理解し、それを正確に適用する」ことではなく、「パターンを理解し、チームメンバーと共通認識を持ち、そこから議論を開始する」 ことであると考えています。

しかし上記のような状況では、この「チームメンバーと共通認識を持つ」ことが難しい場合もあるのではないでしょうか。ましてインターネット上の知らない人同士の議論ではいつまで経っても話が噛み合わない場面も少なくないと思います。

そのような議論をする際の拠り所のひとつとして、この記事が活用されれば良いと思っています。

GUI アプリケーションにおける一貫した課題

MVC, MVP, MVVM の個別の説明に入る前に、これらのどのアーキテクチャにも共通する 「GUI アプリケーションで解決すべき 2 つの課題」 について整理しておきたいと思います。

データと UI の適切な同期

1 つ目は 「データと UI の適切な同期」 です。

GUI アプリケーションでは、データベースから取得したデータを画面に表示したらそれで終わりではありません。ユーザーの操作をはじめとする何らかのきっかけでデータに変化があった場合、画面を更新してその内容を過不足なくユーザーに見せてあげる必要があります。

しかしその流れをプログラムで実現しようとした場合に、ひとつの問題が発生します。それが 「同じデータがシステムの至るところでコピーされる」 という点です。

Martin Fowler によると、少なくとも以下の 3 つのコピーが発生します。

  1. データベースに保存されたデータ (record state)
  2. データベースから取得されメモリ上に保持されたデータ (session state)
  3. UI コンポーネントが画面に表示するために保持するデータ (screen state)

データベースに保存された record state は、データベースからのフェッチによっていったんメモリ上(データベースアクセスをするロジックの変数上)に session state として保持されます。その後、何らかの方法で UI を実現するコンポーネントに渡されてユーザーの画面上に表示されます。この時 UI コンポーネントが保持するのが screen state です。

データは一度表示されたら終わりではなく、ユーザーの操作によって変更があった場合には逆に UI からデータベースへ、やはり過不足なくそのデータを保存してあげなければなりません。

現代の複雑な GUI アプリケーションでは、「ユーザー操作によってデータが書き換わったが、データベースには保存しない」といった 3 種類のデータを完全に同期すれば良いわけではない場合だったり、「データベースの変更をリアルタイムに UI に反映させる」など UI 更新のきっかけがユーザー操作ではないパターンも考えられるでしょう。

このような課題に対し、「齟齬が出ないように気をつけてコーディングする」といった精神論ではなく「齟齬が発生し得ない設計」を考え、各 state 間でデータが過不足なく同期される仕組みを作る ことが求められます。

MVC 発祥の 1970 年代も、宣言的な UI 構築が発展してきた 2024 年現在であっても、根底にある課題は変わりません。「内部でデータは変更されたけど表示が変わらない」という不具合は今も昔も悩みの種です。

2. UI フレームワークとビジネスロジックの分離

2 つ目が 「UI フレームワークとビジネスロジックの分離」 です。

特にソフトウェアが十分に浸透していなかった 1970 年代ではビジネスはアナログで進行していて、それを自動化・効率化するためにソフトウェアが開発され、そこには文字通りの「ビジネスロジック」が存在しました。

そのようなビジネスロジックはソフトウェアが GUI だろうが CLI だろうが、そのインターフェースとは関係なく存在します。つまり、そのビジネスロジックは UI フレームワークが何であろうと変わらないコードで実装されるべきと考えられてきました。

ビジネスロジックは独立したプログラムとして開発し、UI は任意のフレームワークからビジネスロジックを呼び出す形で動作させる というプログラムの分割によって、「ビジネスに詳しい開発者はビジネスロジックを」「デザインに明るい開発者は UI を」それぞれ開発するという作業の役割分担が可能になります。

また UI フレームワークに依存せずビジネスロジックが実装されることで、一般的にテストが難しい UI 部分を省いた 純粋な「ビジネスロジック」の動作をテストコードで検証できる ようになります。

GUI アプリケーションにおいて、この 2 つの課題は時代を問わずに存在します。これを読んでいるみなさんが使うフレームワークがどのようにこれを解決しているのか、振り返って考えてみると面白いかもしれません。

では、それを踏まえた上でそれぞれのパターンについて考えてみましょう。

3 つのアーキテクチャパターン

先述の通り、この記事では MVC, MVP, MVVM といった、特にモバイルアプリ開発の分野でよく耳にするアーキテクチャパターンについて、提唱された年代が古い順に書いていきます。[1]

記事の内容はなるべく「起源」と言える資料を読みながら書いていますが、一番古い MVC が提唱されたのが 1970 年代、つまり 36 歳である自分(2024 年現在)が生まれてすらいない時代に提唱されたもので、当時の開発事情はよく知らない中での理解になるため、誤り等がありましたらぜひコメントください。

なお、GUI アーキテクチャパターンの変遷については 20 年近く前、2006 年に Martin Fowler が以下の記事にまとめています。[2]

https://martinfowler.com/eaaDev/uiArchs.html#ModelViewController

より「起源」に近い年代に偉大な人によって書かれた記事ですので、もしこの話題に興味があればこちらを読み込むことを強くオススメします。

MVC

MVC が提唱されたのは 1970 年代、Smalltalk による GUI アプリケーション開発で用いるアーキテクチャパターンとして、Smalltalk の開発元と同じ Xerox PARC で考え出されました。

MVC パターンを知るための一番古い資料が Glenn E. Krasner / Stephen T. Pope によって書かれた A Cookbook for Using View-Controller User the ModelInterface Paradigm in Smalltalk-80 という論文のようです。 [3]

https://www.lri.fr/~mbl/ENS/FONDIHM/2021/papers/Krasner-JOOP88.pdf

(2024.10.30 修正) 「起源」としては以下の資料が適切だという指摘をいただきました。また、以下の資料によると MVC は GUI アプリケーションを前提としたものではなさそうとのことで、詳しくはちゃんと読み込んでからさらに加筆、修正予定です。この記事の MVC に関しての記述は以上を念頭に読んでいただければと思います。

https://folk.universitetetioslo.no/trygver/themes/mvc/mvc-index.html

(/修正ここまで)

Martin Fowler の先述の記事にも、Model View Controller のセクションで説明されています。

https://martinfowler.com/eaaDev/uiArchs.html#ModelViewController

MVC についてはこのあたりの資料を読みながら理解を深めてみようと思います。

Model / View / Controller

MVC パターンではデータベースから UI に至るまでの流れを M(Model) / V(View) / C(Controller) という 3 つのコンポーネントで分割します。

Model は先述の「ビジネスロジック」を実装します。つまり、UI コンポーネントに依存せず純粋にそのソフトウェアが担うビジネスをプログラムに落とし込んだロジックを実装するのが Model です。

そして画面への描画を担当するのが View、その橋渡しをするのが Controller という説明が続くのですが、ここでいう View と Controller は、現代のわれわれが考えるようなものとはニュアンスが異なることに注意が必要です。

MVC における View と Controller

MVC における View とは、テキストや入力ボックス、チェックボックスといったひとつひとつの UI パーツを表します。そして、そのひとつひとつの細かな View に対して Controller が 1 対 1 で紐付きます。さらに、ひとつの Controller が操作する Model は常にひとつです

Each view may be thought of as being closely associated with a controller, each
having exactly one model

これはまだ GUI アプリケーションが複雑ではなく、入力フォーム上のひとつの入力ボックスの値が変更した時、それに対応するデータベースの該当レコードの該当カラムを更新する程度の操作で事足りる場合が多かったことが背景にあるようです。[4]

シンプルなMVCの図

たとえば「書籍名」の入力欄 (View) に値が入力されたら、その入力は対応する Controller が受け取り、「書籍」データを管理する Model に対して書籍名の変更処理を呼び出します。その結果 Model はデータベースの該当箇所を更新する、という流れです。

ひとつの View / Controller はひとつの Model のみを操作しますが、別の View / Controller が同じ Model を操作することも考えられます。

View/ControllerとModelがn対1の図

図のように、「書籍名」だけでなく「著者名」も同じく「書籍」を表す Model を操作します。Model は変更内容に応じてデータベースを適切に更新します。

通知による View の更新

ここで、それぞれの入力値が入力値が別の View の表示内容に影響を与える場合を考えてみます。例えば、「著者名と書籍名がどちらも入力されたら『公開』ボタンが活性化する」という、よくある UI について考えてみます。

公開ボタンは書籍名と著者名が入力されたら活性化する

MVC において、入力欄の値の変更は Controller が受け取って Model の更新処理を呼び出すことによって機能します。Controller が他の View や Controller を直接操作することはありません。それによって View 同士、Controller 同士を疎結合にします。

では公開ボタンはどのようにして入力欄の変化を検知するのかというと、ここで出てくるのが「通知(Broadcasting)」という方法です。

When a model has changed, a message is broadcast to notify all of its dependents about the change.

公開ボタンは Model の変更を待ち受け、Model はデータの変更を通知することにより「著者名と書籍名が入力されたら公開ボタンが活性化する」が実現します。

Modelの変更はViewに通知される

MVC の図

このような流れを簡略化すると、よく見る MVC の三角形の図が完成します。

MVCの図

View / Model / Controller をすべてひとつずつと考えるのではなく、ひとつのコンポーネントに対して別のコンポーネントがいくつ紐づくのか、ひとつひとつのコンポーネントが担当する単位に着目してみることで、MVC の作り方が見えてくるのではないかと思います。

MVC のバリエーション

さて、ここまで A Cookbook for Using View-Controller User the ModelInterface Paradigm in Smalltalk-80 の内容を頼りに MVC の説明をしてきましたが、現代で議論される MVC は必ずしもこの形ではない場合があります。

Martin Fowler も 2006 年の段階でこの「MVC の亜種、もしくは誤解された形がたくさん発生している」ことについて言及しています。

Probably the widest quoted pattern in UI development is Model View Controller (MVC) - it's also the most misquoted. I've lost count of the times I've seen something described as MVC which turned out to be nothing like it. Frankly a lot of the reason for this is that parts of classic MVC don't really make sense for rich clients these days.

ここで述べられているように、形を変えた MVC が発生するのには理由があります。

時代が進むにつれソフトウェアに求められる要件はリッチに、複雑になり、それを開発するためのフレームワークも変化し、なんなら GUI アプリケーション以外でも MVC のアイデアが取り入れられるようになり、結果としてそれを実現する具体的な手法は当初の Smalltalk による GUI アプリケーション開発とは異なる形になってきた、というわけです。

冒頭でも議論した通り、アーキテクチャパターンの目的はソフトウェア開発における課題を解決することです。「起源」に忠実に実装することではありません。状況が変われば課題も変わり、その解決方法も変わってしかるべきです。

問題は、そのようにさまざまに形の変わったアーキテクチャをひとまとめで "MVC" と呼び続けていることです。

"MVC" が指すものが人によって異なれば、当然議論は前に進みません。この記事を書いた一番のモチベーションは、このような状況において 「議論を進めるためのいったんのスタート地点」を提供する ことにあります。


MVC については以上です。次に MVP について見てみましょう。

MVP

MVP は 1990 年代に IBM の子会社である Taglint で提唱されました。MVP の詳細については Taglint の VP & CTO である Mike Potel が書いたドキュメントが以下に公開されています。

https://www.wildcrest.com/Potel/Portfolio/mvp.pdf

また Martin Fowler の記事にも Model_View_Presenter というセクションで説明されていますので、そちらも参考にしてください。

https://martinfowler.com/eaaDev/uiArchs.html#Model-view-presentermvp

MVC から MVP へ

MVC は元々 Smalltalk による GUI アプリケーション開発を前提としたアイデアでした。

Model / View / Controller というレイヤーに分け、役割分担を明確にし、さらに依存の方向やデータの送受信方法を規定することで GUI アプリケーションの開発にひとつの「パターン」を提供したのが MVC でしたが、逆に言えばそれ以上のことは特に規定されていませんでした。

しかし時代が進んで 1990 年代、機器の進化によってソフトウェアにできることが増え、UI はさらにリッチに、複雑になり、Model や View に求められる役割が増えてきました。MVC パターンに沿って開発をしても、 「じゃあいろんな処理が必要な Model の中はどう実装すれば良いのか」「複雑な View のコードをどう整理すれば良いのか」といった課題 が新しく発生します。

そこで Taglint では、そのようなインタラクティブな操作を伴う幅広いアプリケーションに適用できるアプローチを考えます。 MVC の内容を Smalltalk に限定せず一般化し、さらに複雑な GUI アプリケーションにおける「パターン」を整理し、MVC をベースに役割分担を細分化して定義したアーキテクチャパターンが MVP です。

課題を 2 つに分割する

この細分化を説明する上で、Potel はまず課題を以下の 2 つに分割しています。

  • データの変化をどのように管理するのか (Data management)
  • どのようにユーザーはデータを操作するのか (UI)

MVC の時代から進んでアプリケーションが複雑になり、UI はデータベースの構造をそのまま可視化したものとは限らなくなりました。

他方でユーザーの入力は対応するテーブル・行・列の更新だけに止まらなくなり、Undo / Redo 機能のように ひとつの操作が更新するデータが状況によって異なる ことも考えられます。ショートカット機能など、 異なる操作が同じ結果をもたらす 場合もあるでしょう

「ユーザーの入力」自体も、キーボードによる文字入力やマウスのクリックにとどまらず、ジェスチャー、「ディスクを挿入した」などさまざまです。[5]

This is more than just the drawing of the object on the screen and the mouse and keyboard events but also what semantic operations are enabled, what user actions are recognized, what gesture language is employed, and what feedback is given.

そのように複雑化したデータ操作、UI 操作の両方をより安全に扱えるよう、この 2 つに対してそれぞれ「3 つの質問」を投げかけることで解像度を上げた議論をしています。

Data management に対する 3 つの質問

データ管理に対する 3 つの質問は以下の通りです。

  1. 自身のデータは何か ("What is my data?")
  2. どのようにデータを特定するか ("How do I specify my data?")
  3. どのようにデータを更新するか ("How do I change my data?")

1 については MVC における M と同様です。ひとつの Model が管理するデータがどのような構造のものなのか、という質問です。これは通常データベースと同じ形になると言えそうです。

2 についてですが、Model が保持するデータは単一の文字列や数値とは限りません。たとえば「記事一覧」というデータであれば、そこには Article オブジェクトのリストが存在し、その Article オブジェクトにも authortitle といった複数の値が保持されます。そのようなデータ構造に対し、 ある UI 操作における変更箇所をどのように特定するのか 、というのがここでの問いになります。

3 は データの変更内容を抽象化し、そこにどのような名前をつけるのか 、という問いです。「Undo / Redo する」「コピーする」「空の記事を新規作成する」などが考えられそうです。

MVP では、従来の MVC では "Model" としてひとまとめにされていたデータ更新処理をこのような 3 つの関心ごとに整理し名前をつけました。それが 1. Model, 2. Selection, 3. Command です。

UI に対する 3 つの質問

同様に、UI に対しても 3 つの質問が投げかけられます。

  1. どのようにデータを表示するか ("How do I display my data?")
  2. どのようにイベントとデータ更新を紐づけるのか ("How do events map into changes in my data?")
  3. どのようにすべてを紐づけるのか ("How do I put it all together?")

それぞれ見ていくと、1 はおそらくイメージ通りかと思います。Model から受け取ったデータをどのように画面に表示するか、という点で、これは MVC の V と変わりありません。

2 は ユーザーの入力をどのように受け取るのか 、という点です。ユーザーの入力はマウス操作やキーボートのタイプだけではなく、ボタンのクリック、スイッチのトグル、ドラッグ&ドロップ、ペンによる手描き、マイク入力などさまざまで、それぞれの入力に合った扱いをプログラム上でする必要があります。

そして ここまでのすべてをどのように紐づけるのか を考えるのが 3 です。

このような UI に関する役割分担を MVP では 1. View, 2. Interactor, 3. Presenter と呼んでいます。MVP の "P" 部分はまさに最後の Presenter を指します。

MVPの全体図

このように、ベースとなる形は MVC と変わらないものの、UI からデータベースまでの流れをさらに高い解像度で役割分担し、MVC と区別する形で名前をつけたアーキテクチャパターンが "MVP" であるわけです。

では、中心となる Presenter についてさらに詳しくみていきましょう。

Presenter

Presenter は図の中の位置だけ見ると MVC における Controller の代替となっているように読み取れます。

先述の通り MVC におけるひとつひとつの Controller が表す単位は「ひとつひとつの細かい UI パーツ」であり、画面内に存在するいくつもの Controller がひとつの Model を操作することによって画面を作り上げるのが MVC のイメージでした。

しかし、以下のように書かれている通り、MVP における Presenter はより大きな役割を与えられています。

The presenter then represents the traditional "main" or "event loop" part of the application, creating the appropriate models, selections, commands, views, and interactors, and providing the business logic that directs what happens when, like a traffic cop or orchestra conductor.

Presenter は

  • main やイベントループに相当し、
  • 複数の Interactor からイベントを受け取り、
  • Interactor ごとに Command を紐付け、
  • 「どのようなイベントが発生したらどのコマンドをどう呼び出すか」を判断する

という、アプリケーション全体の「交通整理」をするものである と書かれています。

図だけ見ると MVC における Controller と同じもののように見えますが、実際に想定される役割の規模は全く違うものであることがこの記述からわかります。

Client/Server アプリケーションにおける MVP

MVP: Model-View-Presenter The Taligent Programming Model for C++ and Java では、MVP が単純な GUI アプリケーションのためのアーキテクチャパターンにとどまらず、Client/Server 型のアプリケーションでも同じ発想が流用できることを説明しています。

つまり、Client を UI 側と見立て、Server を Model 側と見立て、その橋渡しを Presenter が行うことにより、システム全体として MVP を実現するというものです。

Client/ServerにおけるMVPの図

クライアントでは View がサーバーから受け取ったデータを画面に表示し、Interactor で検知したユーザーの入力をそのままサーバーに送信します。

サーバーではその入力を解釈し、データ操作の内容とその対象を Command, Selection のそれぞれで行い、最終的には Model が持つデータを更新します(と同時に必要に応じてデータベースのデータを書き換えます)。その結果 Model のデータは通知によりクライアントの View へ渡され、画面が更新される、という流れです。

Smalltalk による GUI アプリケーションを想定して考えだされた MVC と違い、MVP は幅広い用途に活用可能な抽象的なアーキテクチャパターンとして考えられている ことがこのことからも見えてきます。

MVP のバリエーション

このように MVC の各レイヤーをさらに細分化し、UI からデータベースまでの流れをより詳細に設計した MVP ですが、ここでも MVC と同様に「バリエーション」の問題が発生します。

Martin Fowler によると、その論点のひとつが「Presenter が View を操作すべきかどうか」とのことです。

One of the variations in thinking about MVP is the degree to which the presenter controls the widgets in the view.

Potel の書いたドキュメントを読む限り、View は Model の値の変化を監視することによって表示内容を更新し、それ以外の経路での表示内容の変更については特に言及されていません。

しかし、「Presenter が(も) View の見た目を更新できる」方が、たとえばアニメーションのようなデータベースの操作を伴わない UI 更新であったり、テストの観点などから都合が良いとする考え方が発生しました。

特に、View が自身で見た目の更新を行わず、必ず Presenter が View の表示内容を管理するアプローチは Passive View と呼ばれ、その概要が以下のページで紹介されています。

https://martinfowler.com/eaaDev/PassiveScreen.html

いずれにしても、MVP についてもやはり(というより MVP も幅広いシステム開発で用いられることを考えれば当然ですが)それを適用するソフトウェア要件やフレームワークによってさまざまに形を変えて取り入れられ、人によって "MVP" に対する認識にバラつきがあると言えそうです。

MVVM

最後に MVVM です。MVVM はモバイルアプリ開発においては一番議論の的になるアーキテクチャパターンなのではないでしょうか。

https://learn.microsoft.com/en-us/dotnet/architecture/maui/mvvm

MVVM 2005 年に Microsoft の John Gossman によって考え出されたアーキテクチャパターンです。

ただし以下の資料にある通り、MVVM の発想は 2004 年に書かれた Martin Fowler の Presentation Model を元にしています。

https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/february/patterns-wpf-apps-with-the-model-view-viewmodel-design-pattern#the-evolution-of-model-view-viewmodel

Gossman introduced MVVM as a standardized way to leverage core features of WPF to simplify the creation of user interfaces. In that sense, I consider MVVM to be a specialization of the more general PM pattern, tailor-made for the WPF and Silverlight platforms.

これを読むと、MVVM は Martin Fowler の Presentation Model パターンのアイデアを WPF や Silverlight プラットフォームでのアプリ開発に特化させたものであることが書かれていますので、「起源」という意味ではこの Presentation Model パターンの方を解説するのが適切と言えるでしょう。

以下、この記事では Presentation Model パターンに着目してその特徴を説明していきます。

Presentation Model

Presentation Model パターンは、先述の通り Martin Fowler が以下の記事で紹介したアプローチです。

https://martinfowler.com/eaaDev/PresentationModel.html

ひとつの画面に含まれる View が多様化し、あるイベントが画面内の複数の View に影響を及ぼすようになり、UI の状態は必ずしも Model から受け取るデータに依存しない場合も増えてきて、さらに Model のデータをどのように UI で表現するかも複雑になってくる。そのような状況において、View のコードは極力「表示内容」に限定させる必要性が高くなってきました。

そこで考えだされたのが Presentation Model パターン で、これは View において発生するロジックをすべて引き受け、View は「何も考えずに」View Model の内容を表示し、また View で発生する一切のイベント処理を引き受けるのが Presentation Model というオブジェクト です。

Presentation Model の役割はユーザーの入力を受けて呼び出すべき Model を判断することです。MVC の頃とは違い、呼び出すべき Model は複数あるかもしれません。

Presentation Model は View の仕事を引き受けますが、だからといって View のコードをそのまま書き移せば良いというわけでもありません。

Instead it is easier to consider Presentation Model as an abstract of the view that is not dependent on a specific GUI framework.

Presentation Model は View で発生するロジックを特定の GUI フレームワークに依存しない形で抽象化 し、あくまで「この画面ではこのような時にこのような Model の操作が行われる」「Model からこのデータが渡された時、この画面で表示すべき値はこれである」を表現することに徹します。それにより、GUI フレームワークに依存せずテストが可能で、また UI の構造が変わったとしても使いまわせる形を保ちます。

Presentation Model における View

Presentation Model パターンにおける View に着目すると、MVC や MVP とはまた違った役割やイメージが見えてきます。

MVC や MVP ではひとつの View はひとつの UI パーツを表していたのに対し、Presentation Model パターンにおけるひとつの View は「1画面 (Window)」を表します。

ひとつの Presentation Model はひとつの画面で発生する操作や表示すべき値の保持を担当します。そして以下で述べられている通り、ひとつの View は常にひとつの Presentation Model を必要とします。

While several views can utilize the same Presentation Model, each view should require only one Presentation Model

つまり、Presentation Model パターンにおける View の単位は 1 画面であることが、この説明からもわかると思います。

Presentation Model ではひとつひとつの UI パーツをまとめた Window を View ととらえ、そこで必要になる状態 (state)や Model に対する操作の一切を Presentation Model が引き受け、View は「Presentation Model と完全に同期して UI を更新するもの」の役割に徹する、というわけです。

また、MVC や MVP と違い、View は Model の値を直接受け取りません。Model の保持する値に変更があった場合に通知が発行されるところは変わりませんが、その受け取り先は View ではなく Presentaion Model となる点に注意が必要です。

Model の変更を Presentation Model が受け取り、受け取ったデータを元に Presentation Model は View がss表示するべき値を生成して保持し、その値を View と同期して UI を更新することにより、Presentation Model パターンは機能します。

Presentation Modelのイメージ

View と Presentation Model の同期

Presentation Model パターンにおける一番の課題は View と Presentation Model の同期 です。

Probably the most annoying part of Presentation Model is the synchronization between Presentation Model and view.

Presentation Model は常に View が表示すべき値をそのままの形で保持します。そして、その値が変更した場合は過不足なく View に同期する必要があります。

Presentation Model が保持する値は Model の変更だけでなく View が発するイベント(例えばチェックボックスに対するクリックなど)でも変化しますので、Presentation Model は Model / View の両方からの変更を受け付けて保持する状態を更新し、それを View に同期しなければならないため、「データは変わったけど UI に反映されない」問題が発生しないように仕組みを作る必要があります。

「View に同期する」とは言ってもその View の更新方法は GUI フレームワークによって様々ですので、具体的な実装方法はそれに合わせて異なります。ただし、どのような場合であっても Presentation Model が保持する状態を過不足なく View に反映させる ことが不可欠になります。

「過不足なく」ですので、足りなくてもダメですし、逆に状態の変化に対して不必要な回数の View 更新が発生するのもパフォーマンスの面から避けたいところです。

Martin Fowler はこの問題はいずれフレームワークが解決してくれると書いています。

Ideally some kind of framework could handle this, which I'm hoping will happen some day with technologies like .NET's data binding.

そしてこの予想の通り、現代では、例えば私が普段利用する Flutter では「状態の変化に同期して UI が更新される」「状態の変化を必要以上に通知しても、フレームワークが必要最低限の再描画を判断してくれる」などの点はフレームワークが解決してくれています。時代の流れを感じますね。


以上が Presentaion Model パターンの説明です。

私たちが目にする MVVM パターンは、この Presentation Model パターンを Microsoft の WPF や Silverlight アプリケーション向けに最適化したものであることは先述した通りですので、現代において "MVVM" と言った場合(且つ、MAUI ではないフレームワークでの GUI アプリケーション開発の場合)はいったんこの Presentation Model パターンの内容を思い出していただきつつ、Presentation Model を ViewModel に読み替えていただければ良いと思います。

まとめ

とても長くなりましたが、以上が MVC, MVP, そして MVVM(Presentation Model)各パターンの「起源」の説明になります。

いずれのパターンも何十年という歳月を経る中で、またさまざまなフレームワークやソフトウェア開発で取り入れられる中で、発祥時とは別の形、別のルールで説明されるケースが少なくありません。

繰り返しですが、問題は「形が変わること」ではなく「人によって認識が異なることを踏まえずに議論すること」 だと私は考えています。

目の前のソフトウェアのアーキテクチャについて考える上でアーキテクチャパターンを参考にすることは少なくないと思いますが、その際「自分はどのようにこのアーキテクチャパターンを理解しているのか」「相手はどのように同じアーキテクチャパターンを理解しているのか」を必ず確認することが円滑な議論のために重要だと思います。

脚注
  1. というのも、自分がモバイルアプリ(特に Flutter アプリ)の開発者であるためです。Web はどのような状況なのか、もし詳しい方がこの記事を読んでいましたらコメントで教えてください。 ↩︎

  2. つまりこの頃にはすでに MVC も MVP も MVVM もあったということですね。それが今でも話題に上がることを考えるとこれらのパターンが与えた影響の大きさが伺えます。 ↩︎

  3. 他にありましたら教えてください。(追記)案の定ありました。内容を修正します。 ↩︎

  4. 繰り返しですが、自分はこの時代の開発について何も知らない状態ですので、参照している資料から読み取れた範囲での推測になります。 ↩︎

  5. このあたりは具体的にどのようなものを指しているのかイメージできませんでしたので、原文をそのまま載せるに留めます。 ↩︎

GitHubで編集を提案

Discussion