一体化したView/Modelと、MVVMとの比較
Model、Viewについて
例:
1 + 2 = 3 の計算処理が、Model
の役割
1 , 2 を入力し、3を表示するのが、View
の役割
一体化したView/Model
とは、コードにおいて、View
の処理とModel
の処理を明確に切り分けられない状態。
アプリの規模と保守の難易度
経験上、アプリの規模・コードの量が増加すると、
-
View/Model
が一体化していると、機能追加や保守が難しく(コードが複雑化)なってゆき、破綻しやすくなる。 -
MVVM
は、一体化したView/Model
と比べると、アプリの規模が小さくても、機能作成と保守が難しい。
しかし、アプリの規模・コードの量が増えても、機能追加や保守の難しさは変わらない。
なぜなら、View
,ViewModel
,Model
をパターン化できる。そのため、全体のコードを理解しやすく構造化できる。また、View
が増えても、全体を把握できる。
Viewの役割とModelの役割が一体化している例
View
のコードビハインドで処理する。
View
のテキストボックス(Para1TextBox
, Para2TextBox
)から値を取得し、計算処理を行い、View
への表示(ResultTextBox
)を行う。
private void CalculateButton_Click(object sender, RoutedEventArgs e)
{
ResultTextBox.Text = (int.Parse(Para1TextBox.Text) + int.Parse(Para2TextBox.Text)).ToString();
}
他のClassから呼び出す
この計算処理を、他のClassから呼び出すにはどうしたら良いでしょうか?
View
のコードを次のようにします。
private void CalculateButton_Click(object sender, RoutedEventArgs e)
{
Calculate();
}
public void Calculate()
{
ResultTextBox.Text = (int.Parse(Para1TextBox.Text) + int.Parse(Para2TextBox.Text)).ToString();
}
そして、他のView
から、計算処理を呼び出してみましょう。
-
View
をnewする。 -
View
のテキストボックスのテキストに値をセット。 - 計算処理を呼び出す。
-
View
の計算結果のテキストボックスから値を取得。
この例では、View
上にあるTextBox
を、値のやりとり(set, get
)に利用しています。
private void Calculate100Button_Click(object sender, RoutedEventArgs e)
{
var view1 = new View();
view1.Show();
for (int i = 1; i <= 100; i++)
{
view1.Para1TextBox.Text = i.ToString();
view1.Para2TextBox.Text = i.ToString();
view1.Calculate();
Result100TextBox.Text += view1.ResultTextBox.Text +"\r\n";
}
}
この例のように、View
とModel
が一体化していると、Model
部分の機能を他のクラスから呼び出すのが難しくなります。従って、テストも困難になります。
この例は、極端ですが、コードビハインドで機能を作成してゆくと、似たような状況になりえます。
ではどうすると良いでしょうか? その回答としてMVVM
があります。
MVVMの要件
-
View
と、Model
とを分ける。 - 単に分けるだけでなく、
View
とModel
とをつなぐViewModel
を間に配置する。 -
Model
は、ViewModel
とView
にコード上は依存しない。 -
ViewModel
は、View
にコード上は依存しない。 -
View
,ViewModel
,Model
は、それぞれの役割に応じた処理にする。
MVVM
の要件は、以上だと考えます。
従って、
-
Dependency Injection
(依存性の注入)はMVVM
と相性が良いですが、MVVM
とは関係ありません。 -
View
のコードビハインドは、必要なら行えばよく、コードビハインドで処理してもMVVM
です。もっとも、コードビハインドを行わない開発方針もあります。
Viewのコードビハインド
View
の.xaml.csファイル
のコードが、View
のコードビハインドです。
InitializeComponent()
以外、書かないようにするという開発方針もあります。その方針を否定はしませんが、
私の考えでは、
View
の処理なら、コードビハインドで処理してもOKです。View
の処理を、XAML
でするかC#
でするかの違いにすぎません。
コードビハインドを避けようとして、コードが何行も増えるのは、本末転倒だと考えます。
MVVMのメリット
MVVM
は、View/Model
の一体化では達成が難しかったことが可能になります。
例:
-
View
を変更する際、Model
の変更は最小ですむ。つまり、View
の変更が容易になる。 -
Model
を変更する際、View
の変更は最小ですむ。つまり、Model
の変更が容易になる。 -
Model
のコードは共通のまま、異なるUIのフレームワークそれぞれに対応するView
を作成できる。例えば、WPF用のView
、iOS用のView
。 -
Model
の作成と、View
の作成との、分業がしやくすなる。 -
Model
のテストをしやすくなる。
MVVMのデメリット
MVVM雑感(2019年)に書いたことですが、デメリットはあまり改善されていません。
- データがどこに流れているのか追跡しにくく、デバッグしにくい。
- 全体的に、コードの量が増える。例えば、コードビハインドなら1行ですむものが、
MVVM
に合わせようとすると何行にもなる。 - 学習コストが高い。
-
MVVM
を実現するためのフレームワークもツールもまだまだ足りない、不便。 -
MVVM
のサンプルや解説はどれがお手本として適切なのか見極めるのが難しい。 - 人によって
MVVM
の考え方が異なり、それを反映した解説も様々である。そのため、初学者は混乱する。 - 小規模なアプリなら、開発効率は低下する。
デメリットも多く、MVVM
が最適な開発方法だとは思いません。ただ、他によさそうな開発方法も思いつきません。ですので、MVVM
を利用しています。
MVVMとデータバインディング
データバインディングは、WPF
の特徴であり、それを利用してMVVM
は構築されています。
しかし、データバインディングの仕組みがなくてもMVVM
は構築できます。
つまり、データバインディングは、MVVM
の必須の要素ではありません。
-
Model
,ViewModel
にPara1
などのプロパティを配置する。 -
UpdateViewModel
,UpdateView
で、
Model
のプロパティ →ViewModel
のプロパティ →View
のTextBox.Text
へ値をセット -
SetParametersOnModel
,SetParametersOnViewModel
で、
View
のTextBox.Text
→ViewModel
のプロパティ →Model
のプロパティ へ値をセット
これにより、MVVM
の要件はクリアします。
UpdateViewModel
, UpdateView
は、.NET
のPropertyChanged
イベント(※)の機能の代わりです。
※ PropertyChanged
は、プロパティの変更を行うと、View
でBinding
したオブジェクトに反映する。
ViewModel
public void UpdateViewModel()
{
Para1 = Model.Para1;
Para2 = Model.Para2;
Result = Model.Result;
UpdateView?.Invoke();
}
View
public void UpdateView()
{
Para1TextBox.Text = vm.Para1.ToString();
Para2TextBox.Text = vm.Para2.ToString();
ResultTextBox.Text = vm.Result.ToString();
}
要は、データバインディングが無くても、データを伝える仕組みがあれば良いわけです。
https://qiita.com/karamage/items/8a9c76caff187d3eb838#comment-9e5f856b2ecdb9b90d80
このように3層に責務を分離した上で
View
とViewModel
間をつなぐために
WPF
ならXAML
などでデータバインディング
SwiftUI
なら@Stateなどでつなぐ
Jetpack compose
ならState
でつなぐ
Flutter
なら状態変化を監視してsetState
などでつなぐ
開発者による実装の違い
WPF
でのMVVM
の実装の仕方は、開発者によって大きく異なります。どの方針を採用するかで、MVVM
の難易度が異なります。
例:
-
View
のコードビハインドを許容するかどうか。 -
ViewModel
でView
のコントロールを処理するか。例えば、引数としてView
のTextBoxコントロールを、ViewModel
のメソッドに渡し、なんらかの処理をする。 -
MessageBox
の表示をどのように処理するか。例:Model
,ViewModel
にサービス(依存性の注入)として実装する。
ただし、少なくとも共通している実装の仕方は、コード上では、「Model
は、ViewModel
,View
に依存しない。ViewModel
は、View
に依存しない。」ということです。
その他
-
MVVM
が提唱された理由の1つは、Windows Forms
でありがちな、Model
とView
との一体化のデメリットを解決するためと考えられます。UI層にモデルの処理を配置すると、テストが困難になり、リファクタリングが難しくなります。
*参照:*2 Design Patterns Model View Presenter -
MVVM
が提案された時は、MVVM
を実現するためのフレームワークも少なく、コードの量がかなり増えました。例えば、Commandをフレームワークなしに実装するのは大変です。今は、ReactiveProperty
などのフレームワークで実現できるので、かなり楽になります。 - 定期的に、
iOS(SwiftUI)
の開発者からMVVM
不要論が出てきます。SwiftUI
でのMVVM
は、WPF
のMVVM
と違いが大きいのでしょうか?
MVVM が提案された頃の考え方
*1
Introduction to Model/View/ViewModel pattern for building WPF apps 2005/10
The term means "Model of a View", and can be thought of as abstraction of the view, but it also provides a specialization of the Model that the View can use for data-binding. In this latter role the ViewModel contains data-transformers that convert Model types into View types, and it contains Commands the View can use to interact with the Model.
意訳
ViewModel
とは、「ViewのModel」の意味で、View
を抽象化したものです。また、同時に、View
がデータバインディングを利用できるようにするため、ViewModel
はモデルの特殊化を行います。ViewModel
は、Model
をView
で表示できるように、データ変換を行います。また、View
がModel
と相互に対話できるコマンドを含みます。
*2
Design Patterns Model View Presenter 2006/8
As UI-creation technologies such as ASP.NET and Windows® Forms become more and more powerful, it's common practice to let the UI layer do more than it should. Without a clear separation of responsibilities, the UI layer can often become a catch-all for logic that really belongs in other layers of the application.
意訳
Windows Forms
のようにUIテクノロジが強力だと、他のレイヤーに属しないといけないロジックが、UIレイヤーに配置されてしまいがちです。
Why is it bad to have lots of logic in the UI layer? The code in the UI layer of an application is very difficult to test without either running the application manually or maintaining ugly UI runner scripts that automate the execution of UI components. While this is a big problem in itself, an even bigger problem is the reams of code that are duplicated between common views in an application. It can often be hard to see good candidates for refactoring when the logic to perform a specific business function is copied among different pieces in the UI layer.
意訳
UIレイヤーにロジックがあると、テストが困難でリファクタリングが難しくなります。
*3
THE MODEL-VIEW-VIEWMODEL (MVVM) DESIGN PATTERN FOR WPF | Microsoft Learn 2009/2
日本語訳
*4
デザインパターン - Model-View-ViewModel の問題点と解決策 |Microsoft Learn 2010/7
日本語訳
MVVMの解説
Wikipedeiaの解説が、よくまとまっているように思えます。しかし、専門用語が多く、それぞれの用語をきちんと理解していないと、この解説も理解できません。