次の方法で共有


MVVM で Model はどのように作るべきなのでしょうか?

質問

2014年3月20日木曜日 6:33 | 2 票

MVVM パターンで考えたとき,
ViewModel が View に公開するプロパティの記述方法にはどのようなものがあるでしょうか.

(1) ViewModel に INotifyPropertyChanged を実装して ViewModel のメンバを直接公開 ← Model の存在意義がない
(2) Model に INotifyPropertyChanged を実装? ← MVVM 的に違う気がする
(3) ViewModel が Model のインスタンスを保持して Model のプロパティを View に公開 ← これ・・・?

自分がこれまでやってきた中で,
最初は (1) のようなやり方で実装していました.
コードだと以下のようになります.

public class MainViewModel : INotifyPropertyChanged
{
    #region INotifyPropertyChanged の実装
    // 省略
    #endregion

    private int member1;
    public int Member1
    {
        get { return member1; }
        set
        {
            if (member1 == value)
                return;
            member1 = value;
            OnPropertyChanged("Member1");
        }
}

しかし,この方法だと Model が存在しません.
そこで次のような実装方法に変更しました.
これが冒頭の (2) に該当します.

public class DataModelBase : INotifyPropertyChanged
{
    #region INotifyPropertyChanged の実装
    // 省略
    #endregion

    private int member1;
    public int Member1
    {
        get { return member1; }
        set
        {
            if (member1 == value)
                return;
            member1 = value;
            OnPropertyChanged("Member1");
        }
    }
}

public class MainViewModel : INotifyPropertyChanged
{
    #region INotifyPropertyChanged の実装
    // 省略
    #endregion

    private DataModelBase model = new DataModelBase();
    public DataModelBase Model
    {
        get { return model; }
        set
        {
            if (model == value)
                return;
            model = value;
            OnPropertyChanged("Model");
        }
    }
}

このコードだと Model も出てくるし,
なんとなく MVVM 使っててカッコいい?みたいな気持ちになります.
しかし,Model に INotifyPropertyChanged を実装するのは
Model が View を気にしないといけないような気がしてなんとなくモヤっとします.
(それに View では Model.Member1 というように記述しないといけない)

悩んだ結果,冒頭の (3) に行き着くわけですが,
これだと Model と同名のプロキシプロパティが大量発生することになります.
その作業が面倒くさいので DynamicObject を使って・・・
という話は検索するとそれなりに出てくるんですが,
皆さん本当にこの方法でコーディングをおこなっているのでしょうか?
それともまた別の方法があるのでしょうか.

自分の周りに WPF を使っている人がいなくて
他の人のコードを見る機会がなく,
実際のアプリケーションのコードを見たことがないため,
どういう方法があるのかよくわかっていません.

皆さんのご意見をお願い致します.

すべての返信 (11)

2014年3月20日木曜日 9:51 ✅回答済み | 1 票

> Model に INotifyPropertyChanged を実装するのは
> Model が View を気にしないといけないような気がしてなんとなくモヤっとします.
> (それに View では Model.Member1 というように記述しないといけない)

私は Modelに INotifyPropertyChanged を実装したクラスを用意しています。特に DataGrid に Model のコレクションをソースとして用意する場面が多いので、INotifyPropertyChanged の実装が必須のケースは多いです。

個人的には、View と Model が直接接続してるといっても、V と M の責務が明確に分離しているなら、Mに INotifyPropertyChanged を実装していても何ら問題ないと考えます。また Model が View を意識して INotifyPropertyChanged を実装するのは、WPF というアーキテクチャを使う以上、止むを得ないと思います。

また ViewModel が不要なレベルの画面(XAMLでほぼ画面が完結し、プレゼンテーションデータを保持する必要のない画面)なら、場合により ViewModel なしの VMパターンもありかなと・・・

ひらぽん http://d.hatena.ne.jp/hilapon/


2014年3月20日木曜日 19:11 ✅回答済み | 1 票

あくまで私の方針であれば。

●View
作業者とのインターフェース。細かなViewの集合体でもある。

●Model
Viewを別のViewに差し替えても変わらない部分のすべて。細かなModelの集合体でもある。
入力の検;はViewが変わったとしても変更されるわけではないのでModel側。
Modelは変更通知機能を有することができる。View側がModelからデータを常に取り出して最新に維持できるのであればなくとも構わないし、最新ではない可能性が前提のWebアプリでは不要な機能である。

●ViewModel
ViewによってはModelに直接接続できない部分の変換を行う。
View専用の表示に合うようにデータ整形する。逆に入力をModelに合うように整形する。
Modelからの変更通知をViewが受け取れる変更通知に変換する。(変換する必要がなければ無駄なのでやらない)
View専用の保持データの管理。例えば、ページ移動しても状態を保存しておくとか。全部の機能が1ページで済むなら必要がないのだからViewのつくりによって変化するデータになるので、ViewModelが管理する。(ブラウザのような複数ページを表示することが目的のアプリの場合はページそのものがモデルで管理されるデータであるからViewModelでは管理しない)
ViewModelでデータ検;を行わうこともできるが、それは入力という行為に対しての反応であってデータに対しての反応ではない。データに対しての反応はModelが行う。(検;エラーという情報のModelが公開される)
ViewとModelを変換なしで接続できるならViewModel無しでいい。
Modelを継承したクラスに機能を追加してViewModelとしてもいい。

結局は、
「WPFで作ってるけどFormsに変更することになると考えた場合に、変わらない部分をまとめるとモデルになる」
「Modelの公開しているデータとコマンドを操作しやすいようにViewをつくる」
「ModelとViewで合わない部分を合うように変換する機能を作るとViewModelになる」
「上記の3つの方針にあうなら、その実装は好きにすればいい」
です。
#VMが注目されすぎて、Modelに実装すればいい機能までVMに実装してしまって、VMにMが混ざってしまうのが混乱する原因だと思う。

まあ、「Modelを継承してViewModelにしてやれば違いを意識せずに使えるよね」「後でVMにするならModelにINotifyPropertyChangedを実装してしまえ」という実装が楽ちんなのでよくやります。

個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)


2014年3月21日金曜日 8:33

人の書いたコードとかでしたら、こんなのとか参考になるかもしれません。

https://github.com/Grabacr07/KanColleViewer

かずき Blog:http://d.hatena.ne.jp/okazuki/


2014年3月22日土曜日 1:00

> 私は Modelに INotifyPropertyChanged を実装したクラスを用意しています。特に DataGrid に Model のコレクションをソースとして用意する場面が多いので、INotifyPropertyChanged の実装が必須のケースは多いです。

私も同じく DataGrid に Model のコレクションを渡す場合があり,
Model に INotifyPropertyChanged を実装しています.

> V と M の責務が明確に分離しているなら、Mに INotifyPropertyChanged を実装していても何ら問題ないと考えます。

そう言われてみると少しモヤモヤがなくなった気がします.
結局,自分の中で View と Model が分離できていると割り切ってしまえるのであればそれでいいということでしょうか.

> また ViewModel が不要なレベルの画面(XAMLでほぼ画面が完結し、プレゼンテーションデータを保持する必要のない画面)なら、場合により ViewModel なしの VMパターンもありかなと・・・

アプリケーションの規模によりけりで,
構成に強くこだわる必要もないということですね.


2014年3月22日土曜日 1:11

参考になる方針をありがとうございます.

> WPFで作ってるけどFormsに変更することになると考えた場合に、変わらない部分をまとめるとモデルになる

というのがかなり的を得ているように感じました.
それにすべての Model が変更通知機能を持つ(あるいは持たない)ようにする必要はなく,
状況に応じて実装すればいいんですね.
なぜか自分は "Model が変更通知をするのか" or "ViewModel が変更通知をするのか" の
二択にこだわり過ぎていました.


2014年3月22日土曜日 1:20

コードのご紹介ありがとうございます.
参考にさせていただきます.


2014年3月22日土曜日 4:08

そう言われてみると少しモヤモヤがなくなった気がします.

結局,自分の中で View と Model が分離できていると割り切ってしまえるのであればそれでいいということでしょうか.

そうですね。考え方としては、同じModel と言っても Forms ・ WPF ・ ASP.NET でまったく同じ設計になるケースは少なく思います。多少なりともフレームワークごとにUIを意識した設計にならざるを得ないケースが多いように感じます。WPF であれば、UIを意識して Model に INofityPropetyChanged を実装していても吝かでないように思えます。

ひらぽん http://d.hatena.ne.jp/hilapon/


2014年3月22日土曜日 18:24

私はwpf、MVVMを使っていないので下記のスレッドで教えていただきました。

http://social.msdn.microsoft.com/Forums/ja-JP/06c5ba3e-76b2-4902-ae04-7da8cbd35be3/mvvmdatagridviewmodel-?forum=netfxgeneralja

当初kisuke0303さんの3の方法を思い浮かべていたのですが、お教えいただいた内容はそうでもなく、

直接Modelにバインディングするものでした。

回答を教えてもらった感想なのですが、VMはレイヤーアーキテクチャのプレゼンテーションモデルと役割が同じではないかと思っています。

また3の方法だとコマンドの保持・実行・ダイアログ等Viewへの通知・画面遷移等アプリケーションの状態管理に加え、データの保持をVMがこなさなければならずVMの責務が重すぎると考えています。

現在はプレゼンテーションモデルを持たせたレイヤーアーキテクチャを使用しています。

レイヤーをまたぐ継承は使っておらず、例えばModelでは、伝票を継承させた売上伝票・入金伝票などを持たせています。

また、Viewとデータソースとの結合点を一点(wpfだとDataContext)としイモずる式に

各データソースをたどれる構造は層間を疎結合に保つのに役に立つことを実感しています。

http://systemartlaboratory.com/


2014年3月24日月曜日 0:59

>(2) Model に INotifyPropertyChanged を実装? ← MVVM 的に違う気がする

出遅れた感満載ですが、みなさんがおっしゃっているように(2)で全く問題ありません。逆に、なぜ、(2)はMVVM的に違うと考えられたのでしょうか? どのようなアーキテクチャであっても、変化したことを知らせるのは変化した自身が素直であって、これはMVVMとは直接関係ない話だと思います。なお、これとは別に、ViewModelにもINotifyPropertyChangedを実装することがあります。これは、ViewがModelではなく、ViewModelが公開しているプロパティと直接バインドすることがあるためです。

★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


2014年3月24日月曜日 12:56

教えていただいたスレッドの回答内容で、
自分と同じやり方をしている方もいて少し安心しました。

> また3の方法だとコマンドの保持・実行・ダイアログ等Viewへの通知・画面遷移等アプリケーションの状態管理に加え、データの保持をVMがこなさなければならずVMの責務が重すぎると考えています。

私も同感で、Model がただのデータ構造になりかねないので
(3) のみとなることはないだろうなと思っています。


2014年3月24日月曜日 13:02

> なぜ、(2)はMVVM的に違うと考えられたのでしょうか?

一度、どこかのサイトで 「Model が View を意識して変更通知を実装するのはおかしい」 というような内容を目にして、
以来、「変更通知 = View を意識している」 という図式が頭の中にできてしまっていました。
今考え直せば、trapemiya さんがおっしゃるように、
変更されたもの自身が変更通知をするのが一番自然でわかりやすいですね。