.NET開発者中心 厳選ブログ記事

MVVMパターンの常識 ― 「M」「V」「VM」の役割とは?

尾上 雅則
2011/05/18

デザイナーと開発者の分業

 「ViewとViewModelが、データ・バインドのみですべての対話を実現する」ということは、何をもたらすのでしょうか。

 それはつまり、「ViewModelの仕様が決まっていれば、ViewとViewModelの分担開発が可能だ」ということです。逆にいえばそれは、ViewとViewModelの対話方法をデータ・バインドのみとしたMVVMパターンだからこそ可能なことです。

 MVVMパターンでは、Viewに表示する情報はプロパティとしてViewModelが公開しますし、「Viewでダイアログを表示させたい」だとか、「Viewを画面遷移させたい」だとかいう揮発性のアクションを実現したい場合にはMessengerを使用します。ViewとViewModelの対話方法は極めて疎です。

 Viewはどういった情報をViewModelの何という名前のプロパティにバインドして表示すればよいのか、ViewModelからどんなメッセージが飛んでくるのか、をViewModel担当側が仕様として公開すれば、十分に分担開発は可能になってきているのです。

 ViewとViewModelの分担開発を行おうとする難易度は、サーバ側に公開されたRESTあるいはSOAP形式のWebサービスとクライアント側のUIを分担開発する難易度と変わらないはずです。

まとめ ― MVVMパターンを適用・カスタマイズするときの留意点

 各種MVVMインフラストラクチャ(LivetMVVM Light ToolkitPrismなど)を積極的に利用する動機としてメモリ・リーク対策も考えられます。

 Modelの項で記載したように、リッチ・クライアントは基本的にステートフルです。そして、MVVMパターンの責務分割方式上、ViewのオブジェクトよりViewModelのオブジェクトの方が長く生存することが多く、またViewModelのオブジェクトよりModelのオブジェクトの方が長く生存することが多くなってきます。どちらも寿命が長いオブジェクトがイベント発行元となり、寿命が短いオブジェクトがイベント受信側となるということになります。

 寿命が短いオブジェクト側がイベント受信側となることは、そのままメモリ・リークのリスクが高いことを指します。

 この問題は、WPF/Silverlightに限って発生することではありません。責務分割型の設計パターンを使用してステートフルなアプリケーション(多くの場合リッチ・クライアント)を作成する場合は必ずといってよいほど問題になります。

 ViewModelのイベントをViewが購読する際、Viewがイベントの購読開始と解除に責任を持つことができればよいのですが、コレクション・コントロールの各項目など、WPF/SilverlightではViewのコントロールの生存期間は完全に開発者が制御できるようにはなっていません(不可能というより、面倒なコーディングを強いられます)。

 しかし、Messenger+トリガー・アクションなどを使用してViewModelとViewの間の対話方法をデータ・バインディングのみに制限すれば、データ・バインディング機構の中で隠ぺいされた形でWeakEventパターンが適用されメモリ・リークは発生しません。

 同じく、ViewModelとModelのあいだのメモリ・リーク問題もあります。Modelのイベントを単純にViewModelが購読しようとした場合、ViewModelが先に消滅しようとした際にメモリ・リークが発生します。ウィンドウなどが対応するViewModelをDataContextプロパティとして持つことが多いため、ウィンドウを閉じる処理などに伴って、この問題に悩まされることがあります。この場合も、ViewModel自身がModelのイベントの購読開始と自身の破棄のタイミングでの購読解除をしっかり管理すればよい話ですが、やはり非常に面倒なコーディングを強いられます。

 しかしMVVMインフラストラクチャ(例えばLivetなど)では、こういった場合の対策として、イベント代替の手段がしっかりと用意されています。

 大きなアプリケーションであればあるほど、「気を付ける」だけの管理は難しくなってきます。特にリッチ・クライアントはステートフルであるが故に、ステートレスなアプリケーションより考慮しなければならない事項が増えます。WPFでは、わたしの知っている開発者の多くがメモリ・リークに悩まされています。

 標準の.NET Frameworkライブラリでの実装難易度が高い現状では、「気を付けるべきこと」に配慮されていて、面倒なコーディングを簡略化してくれ、ビジネス・ドメインに結合しない使い回しのできる実装がすでに用意されている各種MVVMインフラストラクチャ(=Livetなどの補助ライブラリなど)の導入が、開発効率を劇的に上げてくれます。

 多くの開発者にとって.NET Frameworkの標準以外のライブラリを導入することに抵抗があるのは理解できます。.NET開発者の世界では、長らくそういうオープンソース・ライブラリ導入の文化は浸透しませんでした。しかし最近では、Visual StudioやExpression Blendもオープンソース・ライブラリの適用を意識した作りになっています。

 例えばVisual Studioには拡張機能マネージャがあります。オープンソース・ライブラリを簡単に導入できるツール「NuGet」への対応も進んでいます。Expression Blendをインストールすれば、オープンソースであるWPF Toolkitが勝手にインストールされます。WPF ToolkitやSilverlight Toolkitの成果物であるコントロール群が、.NET Frameworkの標準クラス・ライブラリに取り込まれる流れも起きています。

 MVVMパターンを適用しようと思うなら、各種MVVMインフラストラクチャ(LivetMVVM Light ToolkitPrismなど)の導入は必ず検討するべきでしょう。

 今回はMVVMパターンでの各責務の実装方法が、「なぜそういった形を取っているのか」を重点的に解説しました。

 直面した要件に合わせて、工数に合わせて、MVVMパターンの一般的な実装をカスタマイズしなければいけないこともあるでしょう。

 そういったときに、一般的なMVVMパターンの実装方法と比べて、「コマンドをコマンドでなくせば、どういったメリット/デメリットが発生するのか?」「コードビハインドの使用を決断することで、どんなメリット/デメリットが発生するのか?」、それらをしっかりと考えることが最も重要です。

 Modelをステートレスにすれば、そもそもMVVMの本来の目的は果たせなくなるでしょう。こういったことをするくらいなら、そもそもMVVMを意識しない方がよいのです。

  何を崩せば何が失われて、何を残さなければそもそもMVVMパターンであることを保てないのかを、きちんと考えてください。ひいては、「なぜMVVMパターンを採用しようと思ったのか?」をしっかりと考えてください。

 そのための一助となることを願って、今回はこの記事を書きました。End of Article

【筆者プロフィール】
尾上 雅則(おのうえ まさのり)

 .NET案件だけを続けてきたフリーランス。最近はずっとMVVMパターンを追っています。


 

 INDEX
  .NET開発者中心 厳選ブログ記事
  MVVMパターンの常識 ― 「M」「V」「VM」の役割とは?
    1.MVVMパターン概要
    2.View
    3.ViewModel
    4.Model
  5.デザイナーと開発者の分業/MVVMパターンを適用・カスタマイズするときの留意点

インデックス・ページヘ  「.NET開発者中心 厳選ブログ記事」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH