LoginSignup
1
1

一体化した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が増えても、全体を把握できる。

image.png

Viewの役割とModelの役割が一体化している例

参照:CalculationViewOnly プロジェクト

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から呼び出す

参照:CaluculationView プロジェクト

この計算処理を、他の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";
        }
    }

この例のように、ViewModelが一体化していると、Model部分の機能を他のクラスから呼び出すのが難しくなります。従って、テストも困難になります。

この例は、極端ですが、コードビハインドで機能を作成してゆくと、似たような状況になりえます。
ではどうすると良いでしょうか? その回答としてMVVMがあります。

MVVMの要件

  • Viewと、Modelとを分ける。
  • 単に分けるだけでなく、ViewModelとをつなぐViewModelを間に配置する。
  • Modelは、ViewModelViewにコード上は依存しない。
  • 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の必須の要素ではありません。

参照:NoBinding プロジェクト

  • Model, ViewModelPara1などのプロパティを配置する。
  • UpdateViewModel, UpdateViewで、
    Modelのプロパティ → ViewModelのプロパティ → ViewTextBox.Text へ値をセット
  • SetParametersOnModel, SetParametersOnViewModelで、
    ViewTextBox.Text → ViewModelのプロパティ → Modelのプロパティ へ値をセット

これにより、MVVMの要件はクリアします。

UpdateViewModel, UpdateViewは、.NETPropertyChangedイベント(※)の機能の代わりです。

PropertyChangedは、プロパティの変更を行うと、ViewBindingしたオブジェクトに反映する。

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層に責務を分離した上でViewViewModel間をつなぐために
WPFならXAMLなどでデータバインディング
SwiftUIなら@Stateなどでつなぐ
Jetpack composeならStateでつなぐ
Flutterなら状態変化を監視してsetStateなどでつなぐ

開発者による実装の違い

WPFでのMVVMの実装の仕方は、開発者によって大きく異なります。どの方針を採用するかで、MVVMの難易度が異なります。
例:

  • Viewのコードビハインドを許容するかどうか。
  • ViewModelViewのコントロールを処理するか。例えば、引数としてViewのTextBoxコントロールを、ViewModelのメソッドに渡し、なんらかの処理をする。
  • MessageBoxの表示をどのように処理するか。例:Model, ViewModelにサービス(依存性の注入)として実装する。

ただし、少なくとも共通している実装の仕方は、コード上では、「Modelは、ViewModel,Viewに依存しない。ViewModelは、Viewに依存しない。」ということです。

その他

  • MVVMが提唱された理由の1つは、Windows Formsでありがちな、ModelViewとの一体化のデメリットを解決するためと考えられます。UI層にモデルの処理を配置すると、テストが困難になり、リファクタリングが難しくなります。
    *参照:*2 Design Patterns Model View Presenter
  • MVVMが提案された時は、MVVMを実現するためのフレームワークも少なく、コードの量がかなり増えました。例えば、Commandをフレームワークなしに実装するのは大変です。今は、ReactivePropertyなどのフレームワークで実現できるので、かなり楽になります。
  • 定期的に、iOS(SwiftUI)の開発者からMVVM不要論が出てきます。SwiftUIでのMVVMは、WPFMVVMと違いが大きいのでしょうか?

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は、ModelViewで表示できるように、データ変換を行います。また、ViewModelと相互に対話できるコマンドを含みます。

*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の解説が、よくまとまっているように思えます。しかし、専門用語が多く、それぞれの用語をきちんと理解していないと、この解説も理解できません。

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1