導入
パターンを学ぶのと同じくらい、やってはいけない形を知っておくとレビューで危険に気づけます。ここまで作ってきた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(...)やWindowをTaskListViewModelの中で直接呼ぶと、ViewModelはブラウザでは動かせないコード(このコース自体が成立しないコード)になり、単体テストもできなくなる。「確認ダイアログを出す」という操作をIDialogServiceのようなインターフェースに抽象化し、実装はView側(またはWPFに依存する層)に置く - コードビハインドに業務ロジックを書く:
Button_Clickイベントハンドラの中に直接タスク追加処理を書いてしまうと、MVVMで分離した意味が消え、そのロジックはテストできない。必ずCommand経由でViewModelのメソッドを呼ぶ
パターンと同じく、アンチパターンも「知っていれば避けられる」ものです。C#デザインパターンコースのアンチパターン回で扱った神クラス・マジックナンバーと同じ理屈が、MVVMという文脈にもそのまま当てはまります。
まとめ
- Fat ViewModelは業務ルールをServiceへ委譲して防ぐ
- Viewは Binding/Commandだけを通じてViewModelとやり取りする
- ViewModelはUIフレームワークの型を直接知らない(ダイアログ等は抽象化する)
- 業務ロジックはコードビハインドでなくCommand経由で呼ぶ
次章: TDDの総仕上げとして、仕様変更・リファクタリング・統合テストに挑みます。