読者です 読者をやめる 読者になる 読者になる

負け犬プログラマーの歩み

負け組だった人間が今一度PGとして人生の飛躍を模索するも加齢と共に閉ざされる未来に直面しているブログ。

MVVMを少し調べてみる

今働いてるC#の案件は、元々はPGx2人で作るはずが俺1人になったけど、それでも納期が死ぬほど甘いので、とにかく時間が腐るほどある。だから、WPFかつMVVMで作って見ることにした。失敗してもアプリケーションの機能が少ないから、Formで作り直しても間に合うし。初めてVisual Studio 2005より先のバージョンを業務で使えるので、LINQだのラムダ式だのの要素はひと通り使うつもり。.NET 4.0だからC#5.0の非同期処理は使えないけど。

で、AndroidWPFは少し似ている部分もある。MVVMで一度開発してみれば、自分が作ろうとしているMVC+PMのアーキテクチャーに何か新しい知見と着眼点をもたらしそうな気がするんだよね。MVVMは、MVPMの亜流と言われているが、門外漢にさらりと分かる詳しい解説もあまり無かったんで、実際に作ってみないと見えてこないことがある。

以下は開発してみた感想(非定期更新)

  • 巷ではコードビハインド、つまりWPFならXAML.cs、フォームならフォーム.cs、ASP.NETならaspx.csというファイルには、基本的に何も書かないというのが鉄則みたい。WPFでも稲妻マークをクリックして即座にイベント発行できるし、かなり便利なイベントもあるのにコードビハインドに記述禁止って、生産性を下げないのだろうか。
  • ただ、MS推奨がどっちなのかは知らんが、MVVMでの開発に使えるような機能やクラスライブラリが予め用意されているのは事実。イベントドリブンだけの開発しか想定されていないであろうAndroidとはそこが違う。その最たるものがバインディング
  • しかし、そのバインディングは、強力であるとあちこち(MSも自画自賛)に書かれているが、単に文字列でプロパティ名をXAML(XMLもどき)に直接書き込んだら同期してくれるだけの仕様。しかも、C#ソースコードはコメント部分も見て変数名をリファクタリングしたら自動的に修正してくれるのに、XAMLの中は見てくれない。これだったら、正直Androidでもリフレクション使って誰でも実装できるレベルだと思うんだが…。通知もObserverのnotifyObserver辺りと殆ど同じだしなあ。
  • ただ、バインディングを通じてコントロールを触る場合は、別スレッドから実行中でも自動的にUIスレッドで実行してくれるのは割と便利。AndroidだとわざわざUIスレッドにタスク投げないといけないからね。
  • ファイル選択ボタンやフォルダ選択ボタンなど多くの画面に存在する機能や、複数の画面に渡って設置されてはいるけど同じ内容を入力させるテキストボックスなどは、フォームアプリケーションの場合、なかなか複数のコードビハインドにまたがって記述は難しかったかもしれないが、MVVMの場合は、その機能別にVMを作って、DataContextにくっつけられる。再利用性やDRY原則には、WPFの方が向いている気がする。
  • publicなメンバー変数ではなくてプロパティでないとバインディングできない。実際に書いていると、自動実装プロパティでは対応できないケースも多いので、その辺りでかなり手間がかかることも多いと感じた。まあ俺の職場のVSがExpressなんだけど・・・。
  • イベント駆動型を用いず、MVVMだけで全てを書くのは時には難しく生産性も落ちる。特にDataGridなんかは、イベントだと簡単に引数から列と行が取得できるんだけど、同じ事をViewの参照すら持たないViewModelでやると一気に難易度が上がる。つか俺には無理だった。
  • また、XAMLとViewModelだけで全ての描画処理を行うのも困難。DataGridのセルを直接弄りたい場合は、C#で直接叩いた方が効率性が上がる。その場合、ViewModelにView(Window)の参照変数を持たせないといけない。疎結合なんて夢のまた夢。
  • いまのところ、NET環境でMVVMで生産性上がるかというと、いまいち確信が持てない。C#のイベント駆動式は完成度が高いし、それに.NETはpartialクラスがあるので、XAMLに書いたコントロールの参照変数を起動時に全て用意してくれる。Androidはその辺りを自分でやることになるから、MVVMやMVPMの有用性も見出だせられるんだが。
  • AndroidXMLのレイアウトも相当難しかったが、XAMLも非常に敷居が高いつーか訳が分からん。Androidなら今ならテキストも参考文献も相当あるのだが、XAMLは海外サイト頼りになってしまう。日本でのWPFSilverlightの将来性は非常に不安。
  • ここからは言語の話。全体的にJavaC#4.0~5.0では相当の機能差がある訳だが、実際に開発していて頻繁に用いるJavaになくてC#にあるものと言えば、ディレクティブ、delegateとラムダ式と、匿名型の型推論、インデクサ辺りか。逆にJavaの利点ってEnumぐらいだよなあ。
  • Javaのプリミティブ型は、C言語か何かで実装してるのか知らんが、要はオブジェクトですらない。しかしC#はObjectから派生した構造体なので、オブジェクトでいて、それでいて値型だからプリミティブ型と同じパフォーマンス(多分)で使える。
  • オーバーライドの仕様もC#が優れてるが、インターフェイスのメソッドはoverrideを書けない。Javaは一応途中からこの問題には対処して@Overrideアノテーションをつけられるようになったが、C#は、インターフェイス名.メソッドという書き方しかできない。
  • C#のswitchはスループットできないのにbreak;必須という超糞仕様になっている。言語的には非常に洗練されているだけに、ここだけが信じられないぐらい酷い状態。三項演算子を使えということなんだろうか。
  • WPFの本は今では書店で探すのが困難だが、Androidのは腐るほどある。でもこんだけWPFではMVVMとかが提唱されていたのに、AndroidでのMVCやMVPMは局所的な支持しか得られてないっぽい辺りが意味深。


たまに、MVVM(MVPM)に対して、「画面のオブジェクトのレンダリング情報を保管する必要があるからそれを用意するという、ただ自然の手法がMVVM」という説明がされる。これは確かに事実ではあるんだけど、一方でそんなグラフィック重視ではないシステムやアプリケーションの場合、レンダリングについては「状態」ではなくて「手続き」で管理できる場合が多い。

つまり、「○○のボタンを押したら、色が■■になる。この状態で△△をしたら、色が@@になる」みたいな形で、オブジェクトのレンダリングが決まるのであれば、それはVM内でもコマンドで決まる要素である。モデルのプロパティーをラップしたVMのset()だけでレンダリングロジックが決まらないんだったら、それはもうイベント駆動で管理しているのと大差がないだろう。

俺が今進めているMVPMは、こうした手続き型+レンダリング情報保全+MVCで作っている。表向きは、Model→ViewまたはModel→ ViewModel→Modelの「通知」を採用するMVC(MVP、MVVM、MVPM)パターンにおいては異色ではあるけれど。