もくじ
https://tera1707.com/entry/2022/02/06/144447
やりたいこと
私はWindowsのGUIアプリ(WPFやWinUI3)を作るようになってそこそこ時間がたったが、いまだ「ViewModel」がなにをすべきものなのかがいまいちつかみ切れていない。
細かい良し悪しはおいておいて、基本のV/VM/Mの構成は、下図のように、
- View
- 見た目(xaml)と、それにくっついてくるコードビハインド(cs)
- ViewModel
- ビューモデル(cs)
※なにをすべきなのかつかみきれないので説明が書けない
- ビューモデル(cs)
- Model
- アプリに欲しい機能を実装するクラス(cs)
※上図にはまだない
- アプリに欲しい機能を実装するクラス(cs)
という感じ(ファイル構成)になると思う。
WPFをやり始めたころ、
- コードビハインドにはほとんどなにも書かずに、ViewModelに書くのがよい
とか、
- ViewModelにいろいろ書いて、ViewModelを太らせるのはカッコ悪い。薄っぺらいのがいい
とか、調べれば調べるほどいろんなこと、真逆っぽいことをいろんな人や記事がいうので、結局どれが正しいねん、わからん、となったまんまで数年経過して現在に至る。
最近も、職場の方といろいろViewModelの存在意義についてしゃべって、いろいろ気づきがあったので、今現在の自分が思う、ViewModelがやるべきじゃないかと思うことをまとめて、それを表現したコードを書いてみようと思う。
今(2023年)の自分が、ViewModelがやるべきと思うこと
下記のように思う。
ViewModelがやること
- ViewModelは、やっぱり薄い方がいい。
「薄い」とは、最低限のことしかやらず、コード量が少ないということ。 - ViewModelにとっての最低限とは、
- ViewとModelの間の値の意味の相互変換(同じ情報でもお互いが使う型が異なるので、その相互変換)
- Modelが提供するタイミングをViewにつなぐ
(Modelのタイミング(データ更新時等)で、Viewの画面更新の処理を呼んであげる) - Viewが提供するタイミングをModelにつなぐ
(Viewのタイミング(初期化やボタン押下時等)で、Modelの処理を呼んであげる)
ViewModelがやらない方がいいこと
上記の「やること」以外を、やらない方がいいと思う。
具体的に挙げだすといっぱいあるが、見えてる範囲で下記はやらない方がよいと思う。
- 値の保持
- 値を保持しておく必要があるなら、それを行うModel/Serviceのクラスを実装する。
- アプリの仕様の実装
- アプリorアプリ内の機能が行う処理は、ViewModelには書かず、その処理を行ってくれるModel/Serviceクラスに書く。例えば...
- 画面表示時に、〇〇と△△△と□□を読み込む
- 送信ボタンを押したら、〇〇のデータを△△に送信する。
- アプリorアプリ内の機能が行う処理は、ViewModelには書かず、その処理を行ってくれるModel/Serviceクラスに書く。例えば...
その他の関連概念
私のイメージなので、意味が世間一般とは異なるかもしれない。(きっと異なってると思う)
Viewクラス
画面。
Modelクラス
単品の機能を提供するクラス。
ファイル操作のModelならファイルを読み書きする機能、通信のModelなら送受信の機能を提供するが、
その中身の情報の「アプリ」としての意味は、こいつらは考えてないイメージ。
例えば、
- jsonのデータを読み書きするクラス
- 別のプロセスと通信するクラス
など。
Serviceクラス
アプリの「仕様」を実装するクラス。
機能を実装するうえで、Modelクラスを使う。
機能の中に画面表示が必要な場合は、画面表示を行ってくれるViewの処理を呼ぶ。
(直接Viewの関数を呼ぶのではなく、Serviceクラスが生やした関数ポインタに、View/ViewModelの関数を登録して、間接的に読んでやるイメージ)
アプリの仕様を実装するので、Modelたちが扱う情報や処理の流れに、こいつが意味を持たせるイメージ。
例えば、
- 〇〇画面の起動時は、json読み書きModelでjsonから「前回アプリ終了時の〇〇設定」を読み込む。
- 送信ボタンを押すと、通信Modelを使って「ユーザーが入力したメッセージ」を送信する。
図にするとこんな感じ?
お互いの関係
Viewのタイミング(ボタンを押すなど、アプリのウインドウを操作したとき等)で、Serviceクラスの処理を呼ぶことは多いと思う。
逆に、Modelのタイミング(なにか常時監視しているデータが更新されたとき等)で、画面更新をするためにViewを呼びたいときも多いと思う。
そういう場合は、
- ViewにModelがやるはずの処理を直接書いちゃうとか、
- ViewがServiceクラスを飛び越えてModelを直接呼んじゃうとか、
をできるだけ避ける、
また、逆に、
- ModelのタイミングでViewがやるはずの画面表示処理をやっちゃうとか(MessageBoxを出したり)
- Modelが、Serviceがやるはずのアプリの仕様にかかわる処理をやっちゃうとか
をできるだけ避ける。
お互いの処理をどうしても呼びたいときは、上にも書いたように、関数ポインタ(デリゲート)に、委譲する処理を登録してあげて、それ経由で呼ぶようにする。
そのへんを気にしてあげれば、それなりにわかりやすくはなるのではないか。
イメージこんな感じ?
最初に戻って、ViewModelがやるべきことはなにか?
とここまで考えて、趣旨がずれて「わかりやすいGUIありアプリのコードを書くためにはどうしたらいいか」になってしまったが、
その、自分なりのわかりやすいコードを書くためにどうすればいいか、の思考の中に、どうしてもViewModelが出てこない。
ViewModelありきで、「わかりやすいViewModel」を書こうと思うと、上で考えたようなViewModelがいいんだろうなーと思うのだが、「わかりやすいコードを書こう」と考えても、ViewModelがどうやっても、役に立つものとして思考の中に登場してこない。
Viewの処理(具体的にはxaml.cs)のボタンが押された処理(Click)でServiceクラスの処理を呼ぶのと、
ViewModelが、ボタンのCommandにBindingしたコマンドの中でServiceクラスの処理を呼ぶのと、
なにが違うのか?
ViewModeがやるべきこと、のところに書いた、(自分で書いておいて何なのだが)
「 ViewとModelの間の値の意味の相互変換」は、なんでView(xaml.cs)でやってはいけないのか?
WPFのコードをWinUI3とか、全然別のUIフレームワーク(androidとか?)に移植しようと思うと、重要になってくるのか?
自分が、そういう「Viewの差し替え」を行うようなことをしないから、重要性がわからないのか?
わからない。。。
試行錯誤してみる
考えても全然わからないので、
具体的に1個実験アプリを作って、上を表現したコードを書いて、なにか見えてこないかやってみる。
続く。
参考
わんくま同盟
ものすごくわかりやすい。
http://ugaya40.hateblo.jp/entry/model-mistake
→P67に、「Viewの差し替えや抽象化要件が無かったら?」→「コマンドを使わなくてよいし、コードビハインドを書きまくればよい」と判断してもいいとある。
やっぱり、自分が「Viewの差し替え」がほぼほぼ発生しないアプリをずっと作っているから、どうやってもViewModelが大事なものとして呑み込めないのか?
→じゃあ、実際に「Viewの差し替え」をするようなアプリを作ってみてその重要性を感じてみようと思う。追々。