モデルビュービューモデルは、イベント駆動型プログラミングをサポートするUI開発プラットフォーム、特にXAMLおよび.NET言語を使用する.NETプラットフォーム上のWindows Presentation Foundation(WPF)およびSilverlightをターゲットにするためにMicrosoftによって開発されました。それ以来、Angular、Knockout、ExtJSなどの多くのJavascriptフレームワークがこのパターンを採用しています。
ほとんどのソフトウェアパターンと同様に、MVVMには適切な使用法と悪用があります。 MVVMの使用はどのような状況で適切ですか?それはいつ悪いことですか?
[〜#〜] mvvm [〜#〜] は、忠実度の高いUIを使用した複雑なユーザー操作が必要な場合に使用することを意図しています(つまり、 [〜#〜] wpf [〜#〜 ] )。
MVVMは、最新のUI開発プラットフォーム(Windows Presentation Foundation、またはWPF、Silverlight)を対象としています。このユーザーエクスペリエンス(UXi)開発者は、より「従来の」開発者(たとえば、ビジネスロジックやバックエンド開発)。 MVVMのビューモデルは、「基本的にはステロイドの値コンバーター」です。つまり、ビューモデルは、モデルからデータオブジェクトを簡単に管理および使用できるように公開する責任があります。この点で、ビューモデルはビューよりもモデルであり、ビューの表示ロジックのすべてではないにしてもほとんどを処理します。
MVVMは、WPFの特定の機能を利用して、ビューレイヤーから事実上すべての「コードビハインド」を削除することで、ビューレイヤーの開発を残りのパターンから分離しやすくするように設計されています。インタラクティブデザイナーにビューコードの記述を要求する代わりに、ネイティブWPFマークアップ言語XAMLを使用して、アプリケーション開発者が記述および保守するViewModelへのバインディングを作成できます。この役割の分離により、インタラクティブデザイナーはプログラミングやビジネスロジックではなくUXのニーズに集中でき、アプリケーションのレイヤーを複数のワークストリームで開発することができます。
この種のリッチなインタラクションが不要なUIの場合、MVVMは過剰な場合があります。 MVPの方が自然な選択かもしれません。 Webアプリケーションの場合、MVCが適しています。これ以上大きくならない非常に小さなアプリケーション(小さなWinformsユーティリティなど)の場合、分離コードで十分です。
MVVMがトラップになる場合があります。私の経験から、それはよりタスク指向のUIではなくCRUDのようなアプリケーション(データよりもフォーム)を支持します。アプリケーションのバックエンド/その他のレイヤーのアーキテクチャが悪いことを意味しているわけではありませんが、「DDDライト」アーキテクチャを搭載した多くのMVVMアプリケーションを見てきました。バインディングが非常に簡単で、POCO/Anemicドメインオブジェクトを使用してORMとMVVM/Bindingでアプリケーションをセットアップするのが非常に簡単なため、なぜなのか正確にはわかりません。
MVVMは、不十分に設計されたデータバインディングレイヤーのバンドエイドです。特に、WPF/XAMLでのデータバインディングの制限により、WPF/silverlight/WP7の世界で多くの使用が見られます。
これからは、WPF/XAMLについて話していると仮定します。これにより、状況がより明確になります。 MVVMがWPF/XAMLで解決しようとしているいくつかの欠点を見てみましょう。
データ形状vs UI形状
MVVMの「VM」は、XAMLで定義されたプレゼンテーションオブジェクトのセットにマップされる、C#で定義されたオブジェクトのセットを作成します。これらのC#オブジェクトは通常、プレゼンテーションオブジェクトのDataContextプロパティを介してXAMLに接続されます。
その結果、viewmodelオブジェクトグラフは、アプリケーションのプレゼンテーションオブジェクトグラフにマップする必要があります。これは、マッピングが1対1である必要があると言っているわけではありませんが、リストコントロールがウィンドウコントロールに含まれている場合は、ウィンドウのDataContextオブジェクトからそのリストのデータを記述するオブジェクトに取得する方法が必要です。
Viewmodelオブジェクトグラフは、uiオブジェクトグラフからモデルオブジェクトグラフを正常に分離しますが、構築および維持する必要がある追加のviewmodelレイヤーを犠牲にします。
画面Aから画面Bにデータを移動したい場合は、viewmodelをいじる必要があります。ビジネスマンの心の中で、これはUIの変更です。それはすべきです純粋にXAMLの世界で行われます。悲しいことに、それはめったにできません。さらに悪いことに、ビューモデルがどのように構造化されているか、およびデータがどの程度積極的に変更されているかによって、この変更を行うにはかなりのデータの再ルーティングが必要になる場合があります。
表現力のないデータバインディングを回避する
WPF/XAMLバインディングは表現力が不十分です。基本的に、オブジェクトに到達する方法、トラバースするプロパティパス、バインディングコンバーターを提供して、データプロパティの値をプレゼンテーションオブジェクトに必要なものに適合させます。
C#のプロパティをそれよりも複雑なものにバインドする必要がある場合は、基本的に運が悪いです。 true/falseをVisible/Collapsedに変換するバインディングコンバーターがないWPFアプリを見たことはありません。多くのWPFアプリには、極性を反転させるNegatingVisibilityConverterなどと呼ばれるものもある傾向があります。これは警鐘を鳴らすはずです。
MVVMは、この制限をスムーズにするために使用できるC#コードを構築するためのガイドラインを提供します。 SomeButtonVisibilityと呼ばれるビューモデルのプロパティを公開し、それをそのボタンの可視性にバインドすることができます。あなたのXAMLはすばらしく、きれいになりました...しかし、あなたはあなた自身を店員にしました-今度は、UIが進化するときに、2つの場所(UIとC#のコード)で+バインディングの更新を公開する必要があります。同じボタンを別の画面に配置する必要がある場合は、その画面がアクセスできるビューモデルで同様のプロパティを公開する必要があります。さらに悪いことに、XAMLを見て、ボタンがいつ表示されるかを確認することはできません。バインディングが少し重要になったらすぐに、C#コードで探偵の仕事をしなければなりません。
データへのアクセスは積極的にスコープされます
通常、データはDataContextプロパティを介してUIに入るので、アプリ全体で一貫してグローバルデータまたはセッションデータを表すことは困難です。
「現在ログインしているユーザー」の考え方は良い例です。これは、多くの場合、アプリのインスタンス内で本当にグローバルなものです。 WPF/XAMLでは、一貫した方法で現在のユーザーにグローバルアクセスを保証することは非常に困難です。
私がやりたいのは、データバインディングで「CurrentUser」という単語を自由に使用して、現在ログインしているユーザーを参照することです。代わりに、everyDataContextが現在のユーザーオブジェクトにアクセスする方法を提供することを確認する必要があります。 MVVMはこれに対応できますが、ビューモデルはすべて、このグローバルデータへのアクセスを提供する必要があるため、混乱します。
MVVMがフォールオーバーする例
ユーザーのリストがあるとします。各ユーザーの横に「ユーザーの削除」ボタンを表示したいのですが、現在ログインしているユーザーが管理者である場合のみです。また、ユーザーは自分自身を削除することはできません。
モデルオブジェクトは、現在ログインしているユーザーについて知っている必要はありません。これらは、データベース内のユーザーレコードを表すだけですが、何らかの理由で、現在ログインしているユーザーをリスト行内のデータバインディングに公開する必要があります。 MVVMは、現在ログインしているユーザーとそのリスト行で表されるユーザーを構成するリスト行ごとにビューモデルオブジェクトを作成し、そのビューモデルオブジェクトに「DeleteButtonVisibility」または「CanDelete」というプロパティを公開するように指示します(感情に応じて)バインディングコンバーターについて)。
このオブジェクトは、他のほとんどの点でUserオブジェクトのようにひどく見えます。ユーザーモデルオブジェクトのすべてのプロパティを反映し、変更に応じてデータの更新を転送する必要がある場合があります。これは本当に不快に感じます-繰り返しになりますが、MVVMは、このユーザーのようなオブジェクトを維持するように強制することで、あなたを店員にします。
考慮してください-おそらく、データベース、モデル、ビューでユーザーのプロパティを表す必要もあります。あなたとあなたのデータベースの間にAPIがある場合、それはさらに悪いことです-それらはデータベース、APIサーバー、APIクライアント、モデル、そしてビューで表されます。プロパティが追加または変更されるたびにタッチする必要がある別のレイヤーを追加するデザインパターンを採用するのは本当にためらいです。
さらに悪いことに、このレイヤーは、データモデルの複雑さではなく、UIの複雑さでスケーリングします。多くの場合、同じデータが多くの場所とUIで表されます。これにより、レイヤーが追加されるだけでなく、追加の表面積が多いレイヤーが追加されます。
状況はどうだったか
上記のケースでは、私は言いたい:
<Button Visibility="{CurrentUser.IsAdmin && CurrentUser.Id != Id}" ... />
CurrentUserは、アプリ内のすべてのXAMLにグローバルに公開されます。 IDは、リスト行のDataContextのプロパティを参照します。可視性はブール値から自動的に変換されます。 Id、CurrentUser.IsAdmin、CurrentUser、またはCurrentUser.Idを更新すると、このボタンの表示が更新されます。かんたん。
代わりに、WPF/XAMLはユーザーに完全な混乱を作成することを強制します。私の知る限りでは、一部のクリエイティブブロガーがその混乱に名前を付けました。その名前はMVVMでした。だまされないでください。GoFデザインパターンと同じクラスではありません。これは、醜いデータバインディングシステムを回避するための醜いハックです。
(この方法は、さらに読みたい場合に備えて、「関数型リアクティブプログラミング」と呼ばれることもあります)。
結論として
WPF/XAMLで作業する必要がある場合でも、MVVMはお勧めしません。
上記の「どのようにしたか」の例のようにコードを構造化したい場合は、モデルをビューに直接公開し、複雑なデータバインディング式と柔軟な値の強制を使用します。読みやすく、書き込み可能で、保守しやすくなります。
MVVMは、コードをより冗長で保守しにくい方法で構造化するように指示します。
MVVMの代わりに、優れたエクスペリエンスを概算するために役立つものをいくつか作成します。グローバルな状態を一貫してUIに公開するための規則を作成します。より複雑なバインディング式を表現できるようにするバインディングコンバーター、MultiBindingなどからいくつかのツールを自分で構築します。バインディングコンバーターのライブラリーを自分で作成して、一般的な強制型変換のケースを軽減します。
XAMLをより表現力豊かなものに置き換えます。 XAMLは、C#オブジェクトをインスタンス化するための非常に単純なXML形式です。より表現力豊かなバリアントを思いつくことは難しくありません。
私の他の推奨事項:これらの種類の妥協を強制するツールキットを使用しないでください。彼らはあなたの問題のドメインに集中する代わりにMVVMのようながらくたにあなたを押し込むことによってあなたの最終製品の品質を傷つけます。
私はMVVMが本当に好きで、その挑戦がやる気を起こさせ、多くの利点を確認しますが、...
パフォーマンスを維持しながら多くのカスタム動作を追加するために多くのUI /インタラクションコードを必要とするアプリケーションまたはゲームの場合-少しダーティなMVVMを使用する方がよい場合が多い-有用な場合、またはデータ中心ではない場合に使用するコードの相互作用中心の領域。コントロールを作成して、別の親コントロール間で移動したり、キャッシュしたりするとします。
それはかなり急な学習曲線を持っているので、時間がない限り、WPF/SLで多くを開発する計画を立て、Blendに熟練した設計者に、UIのテストコードを書く必要性を感じさせるか、そうでなければ何年もメンテナンスを行うことを期待しますプロジェクト-それは報われないかもしれません。
あまり多くの設計者がBlendを知っているわけではありません。いずれにせよ手動でテストする必要があるため、VMをテストするのではなく、最も重要なバグを見つけるには、すべてのプロジェクトがプレゼンテーション層に自動テストを集中する価値があるわけではありません。
それは本当に投資です。最初にWPF/SLとXAMLの基本を理解し、次にバインディングを正しく行う方法を理解し、vsに何らかの順序で接続し、適切なコマンドを取得し、ほとんどの場合フレームワークを選択する必要があります。ライセンスが原因で問題が発生する可能性があります。sippetsライブラリを効率的にコーディングして構築します。プラットフォームがバインディングで常にうまく機能するとは限らず、必要な動作を実現するビヘイビアーのライブラリを構築する必要があるだけです。
全体的には-すべてのハードルを克服し、パターンにかなり熟練した場合-それはすべて、明快さ、保守性、そして...自慢する権利に見返りがありますか? :)
私は長年、WMV/Silverlightプログラマーとして、MVVMでトレーディングシステムなどの巨大なアプリケーションを構築してきました。
私にとって、年月がたつにつれて、厳密なMVVMは時間を消費し、費用がかかることを学びました。厳密には、「コードビハインドなし」などのルールを意味します。
コードビハインドを持たないことは、最も基本的なフォーム/データベースアプリ以外では不可能です。
デザイナーは初日に標準コントロールでは不可能な何かを指定するので、MVVMでの作業中に対話パラダイムをサポートするには、一連のカスタムコントロールを構築するか、既存のコントロールをラップする必要があります。
私は、数学、慣性、ジェスチャーなどを使用したあらゆる種類のswish UIコントロールとそのハードワークを記述しました。
しかし、これはすべてコードビハインドであり、ビューにはありません。ボタンのクリック、スクロール、ドラッグなどを処理する必要がありますが、すべてが一連のコントロールにうまく組み込まれているため、このコードビハインドは何とかうまくいきます。
そのために一連のコントロールを作成するのではなく、単にコードビハインドと巧妙なUIの要素をビューに記述する方が簡単なことがよくあります。
MVVMの目標は、アプリケーション/ビジネスロジックをUIロジックから分離することです。これを行うには、バインディングシステムとMVVMが最適です。
XAMLデザイナー、C#プログラマー、Blendでの作業、VSでの作業など、宣伝されている他の目標は誤りです。
UIを迅速に再設計できることは、もう1つの誤りです。それは決して起こらない、その時期尚早な最適化。大規模なリワークには、ビューモデルに対して多くの作業が必要です。微調整は1つのことですが、UIの迅速なオーバーホールは不可能です。ビューモデルは相互作用パラダイムに適合している必要があるため、結合は予想よりも緊密です。
私にとっては、MVVMを使用してアプリケーションロジックを分離します(通常、テスト可能なコントローラーとロジックレスビューモデルのセットを使用します)が、UIを美しく見せ、セクシーに動作させるために必要なコードやトリックはありません。汗をかきます。
それ以外の場合、MVVMを使用してすべてを実現する方法のアイデアがあまりに面倒になりすぎて、デザインやクールなアニメーションなどに君臨することになります。
MVVMは、人生のほとんどのことと同様に、どこかにスイートスポットがあります間 2つの極端。
ソフトウェア設計で最も重要なことは、製品を早期に出荷し、どの機能が使用され、どのUIが適切に機能するかを確認し、誰も使用しない製品の美しいコードを作成しないことです。
アプリケーションで大量のデータにリアルタイムでバインドする必要がある場合、MVVMはプロセスを遅くする抽象化を導入しているため、実際に邪魔になる可能性があります。エンジンは現在それほど効率的ではありません。機能強化は進んでいますが。
MVVMは、現状では、検討する必要がある方程式の一部だけではありません。切断されたViewModel間で通信できるようにするには、純粋なMVVMをMediatorパターンなどのインフラストラクチャでサポートする必要があります。
上記のbluczの意見にもかかわらず、MVVMはGoFパターンと並んでいます。パターンを見ると、MVVMは、MVVMとほぼ同時に出現したモデルビューパッシブプレゼンターパターンに相当します。ここでは、MVVMの使用方法を理解していないため、MVVMの複雑さについて不満を言うことがよくあります。
MVVMの問題を発見したもう1つの領域は、それをサポートするように設計されていないサードパーティのテクノロジ(MS DynamicsのUIIフレームワークなど)を組み込む必要がある場合です。 MVVMで作業するためだけに他の技術を「ハッキング」する価値があるかどうかを自問する必要がある場合があります。
Win FormsのようなものをWPFソリューションに混ぜて一致させる場合、ソリューションのその部分はおそらくMVVMにも適さないでしょう。これにより、MVVMが適用されない領域に関するいくつかの情報が得られることを願っています。
次の場合、MVVMを使用しても意味がありません。
それでおしまい。別のプロジェクトで何かをテストまたはデバッグしているのでない限り、WPF/Silverlightで作業するときになぜMVVMを使用しないのか、本当に考えることはできません。 XAMLバインディングが機能する方法のため、WPF/Silverlight開発に理想的なデザインパターンを見つけました。
MVVMの重要な点は、アプリケーション全体がViewModelで実行されることであり、Viewは、ユーザーがViewModelと対話するために使用できるかなりのレイヤーにすぎません。ボタンをクリックしたいのですが、実際にはViewModelメソッドを実行しています。ページを変更したいのですが、実際にはViewModelのCurrentPageプロパティを変更しています。
私はすべてのWPF/Silverlight開発でMVVMを使用しています。小規模でシンプルな単一ページのプロジェクトでも、MVVMの使用方法はプロジェクトのサイズと必要なものによって異なります。 MVVMを使用せずに小さなアプリを1回実行したところ、後悔してしまいました(後で、更新を追加するときにMVVMを使用するようにリファクタリングされました)
MVVMに変換しようとしてコードビハインドされたプロジェクトでクライアントのためにいくつかの作業を行いましたが、それは巨大な混乱でした。すべてのコードビハインドプロジェクトが混乱していると言っているわけではありません。ビューはBlendをエラーまたはクラッシュさせ、設計者に問題を引き起こしました。
私はRoRに手を出して、ASP.NET Webフォームで何もすることをほとんどスキップしましたが、ASP.NET MVCが出てきたとき、私はできる限り学習しようとしました。 。これは別のパターンですが、SL/MVVM開発のIn Actionブックから学んだことの多くを使用しています。
Silverlightで作業を始めたとき、それがどれほど裸であるかに驚いて、それをドレスアップするために利用できる多くのフレームワークの1つを選択する必要があるように感じました。これは、MVVMの学習をあきらめる人々の問題だと思います。
私は、優れた MVVM-Light から始めました。これは、パターンの完全な把握に役立ちました。後でCaliburn Microで作業を始めたところ、ライトが点灯しました。私にとって Caliburn Micro は、ASP.NET Webforms上のASP.NET MVCを使用するようなものです。したがって、小さなサンプルアプリケーションの場合でも、プロジェクト、NuGet CMを作成し、オフにして実行しています。私はViewModelの最初のアプローチに従い、ビューを単純で、Blendで簡単に操作できるようにします。私のVMは、それに慣れていればテスト可能であり、CMを使用すると、複雑な画面レイアウトを簡単に回避できます。
あなたはこの代替案をチェックアウトして楽しむことができます。このオプションは、データバインディング、データテンプレート、およびMVVMのWPF方法を回避します。このオプションは、以前のシンプルで信頼性の高いWinForms Designer.csアプローチに似ています。
WPFコンポジット
http://wpfcomposites.codeplex.com/
ここでは、WPFの簡潔なC#コードビハインドは、グリッドベースのコンポジットを介してUIの上に単純なマトリックスをオーバーレイすることによって生成されます。最新のXAML UIにいくつかのテキストラベルと写真のリストボックスしか含まれていない場合、これを単純なコード行で定義できないのはなぜですか。grid.AddText(guid、x座標、y座標)?これはキャンバス上にはありませんが、グリッド、ドックパネルなどのWPFコンテナー内にあります。WPFは非常に強力です。このアプローチは単にこれを活用します。
開発者は通常、マトリックスやインデックスを気にしません。データ転送オブジェクト(DTO)のGUIDまたはPOCOによって定義された粗粒度のコンテナーレベルから開始し、次にこれらのキー付きコンテナーを潜在的な行と列のより細かいマトリックスで補完しますか?
上記のcodeplexプロジェクトは、ListBoxコントロールから開始してこのアプローチを導入していますが、すべてのWPF複合コントロールをカバーするように拡張されています。たとえば、各listboxitemはGUID(クラスと1対1で関連付けられているコンポジット)で追加されたコンポジットであり、このアイテムの内部には子UI要素(子は、クラスのプロパティに1対1で結び付けられます)は、コードビハインドを介して自由に追加できます。子は、テキストブロックまたは画像の場合があります。
データテンプレートの代わりに、インデックスと明確に定義されたUI要素構造(ベースとしてPresentationFramework.Aeroのテーマにしたリストボックスを強制的に開始する)を活用するという考え方です。この新しいアプローチは、実行できることを意図的に制限しますが、そうすることにより、直観的な、簡潔で堅牢なC#コードビハインドが得られます。スクロールバーをスタイリングするためのコントロールテンプレートソリューションを探したり、単純なタスクのために複数のデータテンプレートを含むソリューションを散らかしたりする必要はありません。