TechParty 2011 in Tokyo RIA アーキテクチャ研究会 尾上 雅則 Agenda 0.自己紹介 1.5分で振り返るMVVMパターン 2.Livetの紹介 3.Livetとメモリリーク 4.Livetライブラリのそのほかの機能 おまけ.LivetとフルMVVM 5.まとめとお願い 0.自己紹介 尾上 雅則(おのうえ まさのり) 27歳 フリーランス MVVMer。他にWindowsForms/ASP.NET/SQL Server/Windows Serverなど Blog - the sea of fertility(http://ugaya40.net) Twitter - @ugaya40 MVVMパターンが何故WPF/Silverlightに必要なのかの復習 1.5分で振り返るMVVMパターン WPF/Silverlightのコレクションコントロール WPF/Silverlightのコレクションコントロールは仮想 化が徹底されている。 ListBox/ListView 1.5分で振り返るMVVMパターン WPF/Silverlightのコレクションコントロール WPF/Silverlightのコレクションコントロールは仮想 化が徹底されている。 TreeView 展開するまで やはり子のインスタンス が無い! 1.5分で振り返るMVVMパターン WPF/Silverlightのコレクションコントロール WPF/Silverlightのコレクションコントロールは仮想 化が徹底されている。 ListBox/ListViewその2 1.5分で振り返るMVVMパターン WPF/Silverlightのコレクションコントロール じゃあどうする? → データバインドで全て解決 各項目に対応するような 表示用のデータバインド用オブジェクト があれば全てデータバインドで解決でき る (チェック状態などもboolとして持つ) むしろ他のアプローチじゃ解決できない! 1.5分で振り返るMVVMパターン WPF/Silverlightの特徴 WPF/Silverlightでは、データバインドがC#コード より優遇されている。 DataTemplateなどは、C#コードからじゃフル機能 使えないとMSDNに明記がある。 コレクションコントロールなどは、ことごとくデー タバインドが無ければ詰みやすい設計となっている。 1.5分で振り返るMVVMパターン WPF/Silverlightの特徴 なのでアプリケーションを適切に責務分割しようと すると、、、、 呼出 ビジネス ドメイン (アプリの機能) プレゼン (アプリの外観) イベント 1.5分で振り返るMVVMパターン WPF/Silverlightアプリの責務分割 なのでアプリケーションを適切に責務分割しようと すると、、、、 外観 呼出 Data Binding 画面 画面を レンダリングする ための情報 ビジネス ドメイン (アプリの機能) イベント 1.5分で振り返るMVVMパターン MVVMパターン この形がMVVMパターンです。見ての通りなんら特 別なパターンではありません。 ViewとViewModelでプレゼンロジックを、Modelで アプリのドメインロジックを分離するのが最大の目 的 依存 Data Binding View ViewModel Model イベント 各責務を見ていきましょう 1.5分で振り返るMVVMパターン View View UIの外観と構造を定義。 XAMLで書くのが基本 ViewModelと併せてプレゼンテーションロジック担当 1.5分で振り返るMVVMパターン ViewModel ViewModel Viewのデータバインディングの為のデータストア。 入力値検証。 Modelからの通知の受信と入力の橋渡し WPFの場合は大抵画面と対応するTree構造になる コレクションコントロールの子も普通ViewModelを持 ちます。 1.5分で振り返るMVVMパターン Model Model アプリケーションの機能そのもの ステートフル Webみたいにリクエスト毎に使い捨てじゃないので当然ス テートフル(プロパティ変更通知を持つ) ただのデータの入れ物ではない データの入れ物だとするとデータを詰めるのは ViewModel?。それはそもそもMVVMの目的に反する。プ レゼンとドメインが分離しない。 1.5分で振り返るMVVMパターン MVVMパターン このセクションで話したことは私の以前の資料の ReWriteというかコピペです。 かなり省略して話したので、疑問のある方などは以 下の資料を参照ください。 MVVMパターンとは? わんくま同盟東京勉強会 #60 セッション資料 The sea of fertility http://ugaya40.net/mvvm/what_is_mvvm.html Livet 2.Livetの紹介 Livet Livet WPF4のための「国産」MVVMインフラストラクチャ ライセンスはzlib/libpng →通常の使用であれば、クレジットなど必要ない →ソース改変使用する場合はクレジットの明記が必要 Livet WPF4 MVVM インフラストラクチャ Project Home : Source Code : http://ugaya40.net/livet https://bitbucket.org/ugaya40/livet/ 2.Livetの紹介 Livetの特徴 – 簡単な導入 Setup.exe一発導入(新規・バージョンアップ共) Visual Studio 2010 Professional以上なら拡張機能 マネージャによる一発導入が可能 Visual Studio Express 2010 C#/Visual Basic対応 2.Livetの紹介 Livetの特徴 – Visual Studio統合機能 各種プロジェクト・アイテムテンプレート・コード スニペット 共にC#/Visual Basic双方提供 2.Livetの紹介 Livetの特徴 – Blendable LivetのView用全機能は、ExpressionBlend4のGUI から、一つの例外もなく記述可能になっています。 直接XAMLを触りたくなければ触らずに済みます。 Blendが本来サポートして いない書式も設定できます 2.Livetの紹介 Livetの特徴 – メインライブラリ(1) LivetのMVVMライブラリ部分は Codebehind付MVVM ViewとViewModel間の通信は極力データバインディ ングにするものの、シンプルなデータバインディング で対応できないものはCodebehindで実装 FullMVVM ViewとViewModel間の通信は全てDataBindingで行 える形式 の双方に対応できるように設計してあります。 2.Livetの紹介 Livetの特徴 – メインライブラリ(2) MVVMライブラリは本来何が問題領域でしょうか? DataBindingの補完 本来は.NET Framework/Silverlightの領域。バインドできないプロ パティ・ダイアログなのどの揮発性の現象 WPF/SLの記述冗長性の排除 本来はC# or DSLの問題。依存関係プロパティもプロパティ変更通知 も現在のC#とは記述の相性が悪い。 MVVM ≠ DataBinding という認識なので Livetは「DataBindingの補完」を最優先して設計しています。 選択肢がいくつかある場合、常にWPFの思想と機構に準じます。 2.Livetの紹介 開発体制 去年の10月からしばらく僕一人で作っていましたが Ver0.96から複数人での開発MLを設けての体制に移行していま す。 • @yfakariyaさん • @azyobuzinさん • @ufcppさん • @aetos382さん • @okazukiさん • @ugaya40 この上なく心強い面子です。 2.Livetの紹介 現状と予定 現在 ver 0.98。次に0.99をリリースし、その次 1.0をもっ て英語化し、Codeplexにて公開して世界のライブラリと 競ってみる予定です。 MS系技術にしては珍しくMVVMライブラリは激戦区。 だからこそ国産ライブラリが今後の標準に影響を与える可能 性だってあります。僕らは一応そこを目指しています。 Prism,MVVM Light Toolkit, Simple MVVM Toolkit,Cariburn,NoMVVM, NotifyPropertyWeaver… その他世界には名の知れたMVVMライブラリが数多くあります。 この問題がもっとも大きいのです 3.Livetライブラリの紹介 ViewModelHelper/Notificator リッチクライアントでは、Webと異なり各責務のライ フサイクルを考慮する必要がある。 これはMVVMに関係なく、リッチクライアントの責務 分割そのものにまつわる宿命。 リッチクライアントはステートフルだから。 責務のライフサイクルを考慮しないとメモリリークの原 因となります まずWPFでの責務間ライフサイクルを紹介します。 3.Livetライブラリの紹介 ViewModelHelper/Notificator ViewとViewModelのライフサイクルが完全に一致す るパターン Data Binding 検索結果画面 ViewModel 検索結果画面 どちらも使い捨てのインスタンスです。ライフサイクル が一致しているので問題はおこりません。 3.Livetライブラリの紹介 ViewModelHelper/Notificator ViewよりViewModelの寿命が長いパターン テスト1ViewModel テスト2ViewModel ここら辺の Viewは勝手 に消える Data Binding ・ ・ ・ でも ViewModelは ある テスト99ViewModel テスト100ViewModel 3.Livetライブラリの紹介 ViewModelHelper/Notificator ViewがViewModelを取り換えるパターン 一覧 仮想化モードRecycleだと、 コンテナが再利用される (つまりコンテナの ViewModelが変わる) 明細 明細画面は一覧で別の アイテムが選択される ごとにViewModelが 変わる 3.Livetライブラリの紹介 ViewModelHelper/Notificator 以上のようにWPFではViewとViewModelのライフサ イクルを開発者が明示的に制御する事が難しくなってい ます。 Data Binding View ViewModel でもDataBindingなら問題ありません。 リソースリークが発生しない仕組があります。 3.Livetライブラリの紹介 ViewModelHelper/Notificator 以上のようにWPFではViewとViewModelのライフサイ クルを開発者が明示的に制御する事が難しくなっていま す。 呼出 View ViewModel イベント しかしDataBindingで処理できない所を この形で処理した場合は注意が必要です。 3.Livetライブラリの紹介 ViewModelHelper/Notificator メモリリークが発生する可能性があります。 呼出 View ViewModel イベント しかしDataBindingで処理できない所を この形で処理した場合は注意が必要です。 3.Livetライブラリの紹介 ViewModelHelper/Notificator そもそもメモリリークは イベント 受信側A public class イベント受信側A { ButtonA.Click += Clicked; イベント 発行元 ButtonA private void Clicked(object sender, RoutedEventArgs e) { //処理 Click イベント公開 3.Livetライブラリの紹介 ViewModelHelper/Notificator そもそもメモリリークは イベント 受信側A public class イベント受信側A { 実は発行元(Button A)が ButtonA.Click += Clicked; 受信側AのClickedメソッ ドの参照を保持している private void Clicked(object sender, RoutedEventArgs e) { //処理 イベント 発行元 ButtonA Click イベント公開 3.Livetライブラリの紹介 ViewModelHelper/Notificator そもそもメモリリークは イベント 受信側A public class イベント受信側A { イベントの登録を解除しないで、 ButtonA.Click += Clicked; 受信側が勝手に消滅しようとし ても発行側に参照されているの private void Clicked(object で消えられない! sender, RoutedEventArgs e) { //処理 イベント 発行元 ButtonA Click イベント公開 3.Livetライブラリの紹介 ViewModelHelper/Notificator つまりイベント由来メモリリークは、 イベントハンドラを明示的に解除しないと、 イベント受信側がイベント発行側から参照されっぱなし で起こる問題です。 これは、開発者側からするとイベント解除すべきタイミ ングの判断しにくいWPFでは大きな問題となります。 3.Livetライブラリの紹介 ViewModelHelper/Notificator ViewとViewModelの関係に焦点を絞って言えば、 ViewModelがイベントを発行 Viewが受信する ので、 Viewの寿命がViewModelより短いパターンで問題に なってきます。 そしてそれがWPFのコレクションコントロールの基本 的な動作です。 3.Livetライブラリの紹介 ViewModelHelper/Notificator Livetでは、この問題に対応するため、 DataBinding機構と同じ仕組みを介する機能を提供して います。 それがViewModelHelper/Notificatorです。 DataBindingと同じで、内部的にはWeakEventパター ンが使用されています。イベントによるイベントハンド ラの参照を参照として認識させないための機構です。 ViewModelHelper/Notificatorを使用すれば、ハン ドラを解除しなくても一応リークはしません。 3.Livetライブラリの紹介 Notificator Notificator Livetでの汎用WeakEvent機構です。 イベント発行側ではコードスニペット : lnevを使用して、普通の CLRイベントとNotificatorを定義します。 コードスニペット : lnevを使用すると、 普通のCLRイベント Notificatorオブジェクト CLRイベントとNotificatorをRaise(発火)させるメソッド の3つがregionで囲まれた状態で生成されます。 3.Livetライブラリの紹介 Notificator Notificator 受信側ではNoticatorHelperを使って、受信ハンドラを 登録します。 もちろん、ハンドラの解除も似たような構文で用意して あります。 3.Livetライブラリの紹介 ViewModelHelper ViewModelHelper LivetでModelからのイベントをWeakEventで受信する 専用の機構です。 以下の4つの機能を備えています。 Modelからのプロパティ変更通知ハンドラの登録・解除 Modelからのコレクション変更通知ハンドラの登録・解除 ModelからのNotificatorオブジェクトを介したイベントのハ ンドラの登録・解除 Modelの変更通知コレクションと同期する、読み取り専用変 更通知コレクションの作成 3.Livetライブラリの紹介 ViewModelHelper ViewModelHelper プロパティ変更通知ハンドラの受信 Modelの変更通知コレクションと同期する、読み取り専用変更通知 コレクションの作成 3.Livetライブラリの紹介 ViewModelHelper/Notificator しかし、今さらですが、 WeakEventパターンは何も考えなくても全ての問題を 解決できる銀の弾丸ではありません。 WeakEventパターンでは不測の発火という問題が発生 しえます。 ハンドラを明示的に解除しなくて良いのだから、ハンド ラの解除はGCまかせになります。 開発者はもうハンドラが反応しないつもりでいても、 GCが回収するまではハンドラが反応するのです。 3.Livetライブラリの紹介 ViewModelHelper/Notificator しかし、LivetのWeakEvent実装の元となったWPFの DataBinding機構は不測の発火に対する対処をデザイン 束縛で解決しています。Livetでも同様の対処をする事 で不測の発火による問題を防ぐことができます。 基本的にViewModelHelper/Notificatorを責務間通信 では使用する ハンドラを解除できるタイミングが明らかな時はちゃ んと解除をする 3.Livetライブラリの紹介 ViewModelHelper/Notificator しかし、LivetのWeakEvent実装の元となったWPFの DataBinding機構は不測の発火に対する対処をデザイン束縛 で解決しています。Livetでも同様の対処をする事で不測の 発火による問題を防ぐことができます。 間にユーザー操作を介さずに、イベント発行元の更新を目 的とするハンドラは書かない(発行元に副作用を及ぼすハ ンドラは書かない) ユーザー操作を挟まないで発行元のイベントを起点に発行元 を更新するのであれば、そもそもすべての処理を発行元でし ていれば良いのです。 この3点を守れば、問題のあるシナリオはないはずです。 3.Livetライブラリの紹介 ViewModelHelper/Notificator ViewModelHelper/Notificatorはコードビハインド付 MVVMであろうが、フルMVVMであろうが、そもそも MVVMでなかろうが、WPF/SLで責務分割を考えた時 に必ず発生する問題に対するソリューションです。 以下のデザイン原則を守る事で、 ViewModelHelper/NotificatorはWPF/SLでのイベン ト由来メモリリークに対する銀の弾丸になります。 ハンドラの解除が出来る時は明示的にする ユーザー操作を介さず、発行元を更新するハンドラを書 かない。 4.Livetライブラリの紹介 そのほかの機能 – バインディングサポート Livet.Behaviors.ControlBinding.OneWay名前空間 には、 すべての標準WPFコントロールの、 標準ではバインドできないプロパティを一定のルールで 抽出し、疑似的に単方向バインドを可能にするビヘイビ アとアクションが含まれています。 Livetアセンブリの7割の容量を占める機能です。 (自動生成です) 4.Livetライブラリの紹介 そのほかの機能 – バインディングサポート ・TextBoxのSelectedTextは本来バインドできない 4.Livetライブラリの紹介 ViewModelCommand/ListenerCommand Livetにおいて、ViewModelがViewに公開するユー ザー操作の事です。コマンドはあくまでもユーザー操 作を介して呼び出されるものです。 操作と、操作の実行可否状態をラップする事が出来ま す。 コマンドはただのメソッドと違い「ユーザー操作」で あるとの意味づけを行えます。 悪しきデザインにならないためにも、私自身はコード ビハインド付MVVMでも意味を明示するために、コマ ンドを使用します。 3.Livetライブラリの紹介 ViewModelCommand/ListenerCommand ViewModelCommand ViewModelがViewに公開する操作とその実行可否オブ ジェクトです。 CanExecuteが引数を取らないのが他のライブラリとの 違い ListenerCommand ViewModelCommandに、Viewからの情報を受け取れ るようにしたものです。 これもユーザー操作を介さないで呼び出さないというデ ザインルールで、様々なトラブルを防ぎつつ運用できま す。 4.Livetライブラリの紹介 コマンドとバインディングサポート まとめ バインディングサポート・ ViewModelhelper/Notificatorは、コードビハインド 付MVVMだろうと、フルMVVMだろうと共通して扱え る機構です。 コマンドはお好みで。 以降はコードビハインドを書かない「フルMVVM」の ための機能の紹介になります。 やはりこれがMVVMの花 おまけ.LivetとフルMVVM フルMVVMとは? コントロールの拡張手段 カスタムコントロール ビヘイビア XAMLベース状態遷移手段 トリガー アクション XAMLベースメッセージング機構 を駆使して、コードビハインド無しのWPFアプリケー ション開発を行うものです。 おまけ.LivetとフルMVVM フルMVVMとは? コードビハインドがないため、ViewはXAMLと各拡張 要素だけになります。各拡張要素がメインのViewと 独立する事は、Viewの各拡張要素がドメインに結合 して不適切な粒度になるのを防ぎやすくなります。 フルMVVM用の拡張要素は、C#で作成し、実際に画 面に組み込む時には全てXAMLで定義可能なため、 ExpressionBlendなどのWYSWIGなエディタの支援 をフルに受けられるようになります。これは理論上デ ザイナとプログラマの分担開発を可能にします。 コードビハインドがないため、ViewとViewModel の責務を各開発者が逸脱しにくくなります。 おまけ.LivetとフルMVVM フルMVVMとは? LivetのフルMVVM用の機能は、もっとも開発に時間 をかけた部分ではありますが、基本から説明している 時間がないので、元となった形式であるPrismとの違 いをメインに駆け足でご紹介します。 おまけ.LivetとフルMVVM メッセージシステム 文字列キーベース Messenger View ViewModel おまけ.LivetとフルMVVM メッセージシステム View定義メッセージ 6.まとめ Livetは、「純国産」のMVVMインフラです。 WPFの思想に則りながら、バインディング機能の補完 を中心に機能を拡張していっています。 コードビハインド不使用にこだわらない設計を心がけ ます。 ユーザーさんが増えてから、フィードバックをいただ くようになり、最近の進化は僕が一人でこそこそと開 発していたころと比べると目覚ましいものがあります。 6.まとめ ぜひ使ってみてください。 使っていただけないとLivetは育ちません。 現時点でももっとも使いやすいWPFインフラになって いると思っています。 よろしくお願いいたします。
© Copyright 2024 ExpyDoc