本文へスキップ
BecomeCoder

C# MVVMコース · 第8章 周辺の道具立て ― DI・ヘルパー・アンチパターン · レッスン33

MVVMのアンチパターン ― やってはいけない設計

ローカル実施

導入

パターンを学ぶのと同じくらい、やってはいけない形を知っておくとレビューで危険に気づけます。ここまで作ってきたTaskListViewModelを思い出しながら、MVVMで特によく起きる失敗を見ていきましょう。

説明

flowchart TB
    A["Fat ViewModel<br/>検証・変換・通信まで全部抱える"] --> R1["→ Serviceへ委譲(第5-6章のTaskService分離)"]
    B["Viewが詳細を知りすぎる<br/>コードビハインドで内部フィールドを直接触る"] --> R2["→ BindingとCommandだけでやり取り"]
    C["ViewModelがUIの型を知る<br/>MessageBox/Windowを直接参照"] --> R3["→ IDialogServiceのような抽象を挟む"]
    D["コードビハインドに業務ロジック<br/>Clickハンドラに直接処理を書く"] --> R4["→ Commandへ移す"]
    style A fill:#ffebee
    style B fill:#ffebee
    style C fill:#ffebee
    style D fill:#ffebee
  • Fat ViewModel(神クラスのMVVM版): 一覧管理も、検証も、DBアクセスも、メッセージの組み立ても――全部ViewModelに書いてしまうと肥大化する。このコースのTaskListViewModelが薄く保てているのは、業務ルールをTaskService(第5章)に委譲しているからにほかならない。ViewModelの責務は「画面の状態を持つ」「操作をCommandとして公開する」の2つに絞る
  • Viewが ViewModelの詳細を知りすぎる: コードビハインドで(DataContext as TaskListViewModel)のように具体クラスへキャストして内部の状態を直接いじると、ViewModelを差し替えられなくなり、テストする意味も薄れる。ViewはBinding({Binding NewTaskTitle})とCommand({Binding AddCommand})を通じてしかViewModelに触れないようにする
  • ViewModelがUIフレームワークの型を知る: MessageBox.Show(...)WindowTaskListViewModelの中で直接呼ぶと、ViewModelはブラウザでは動かせないコード(このコース自体が成立しないコード)になり、単体テストもできなくなる。「確認ダイアログを出す」という操作IDialogServiceのようなインターフェースに抽象化し、実装はView側(またはWPFに依存する層)に置く
  • コードビハインドに業務ロジックを書く: Button_Clickイベントハンドラの中に直接タスク追加処理を書いてしまうと、MVVMで分離した意味が消え、そのロジックはテストできない。必ずCommand経由でViewModelのメソッドを呼ぶ

パターンと同じく、アンチパターンも「知っていれば避けられる」ものです。C#デザインパターンコースのアンチパターン回で扱った神クラス・マジックナンバーと同じ理屈が、MVVMという文脈にもそのまま当てはまります。

まとめ

  • Fat ViewModelは業務ルールをServiceへ委譲して防ぐ
  • Viewは Binding/Commandだけを通じてViewModelとやり取りする
  • ViewModelはUIフレームワークの型を直接知らない(ダイアログ等は抽象化する)
  • 業務ロジックはコードビハインドでなくCommand経由で呼ぶ

次章: TDDの総仕上げとして、仕様変更・リファクタリング・統合テストに挑みます。