ViewModel設計の原則 その2

□導入

今回のブログでは、2番目の原則として「画面定義後、Viewの実装の前にViewModelの(簡易)設計を行うこと」を説明します。順を追って説明するために、まずViewModel設計の優れた例としてMSDN Manazineのサンプルを見てみます。これをベースとして、「画面定義後にViewModelの(簡易)設計を行う必要があること」を説明します。その後、「ViewModelの設計を行った後にViewの実装を行う必要があること」を説明します。 

なお、ここでの説明がどのプロジェクトにも適用可能な正しい内容であるかは分からないことをあらかじめご承知おきください。ここでの内容をそれぞれのプロジェクトで使用するのはもちろん構いませんが、すべて自己責任でお願いします。


□ViewModel設計の例

それでは早速、良いMVVMの設計例として、MSDN Manazineのサンプルを見てみましょう。ここで紹介されているのは、顧客管理のためのサンプルであり、以下のような画面です。

「Model-View-ViewModel デザイン パターンによる WPF アプリケーション」
http://msdn.microsoft.com/ja-jp/magazine/dd419663.aspx
(このMSDN Magazineのサンプルコードは、MVVMを学ぶ上でとても参考になりますので、MVVMを初めて学ぶ際には是非ソースコードまでご覧ください。)



顧客リストをワークスペースに表示


新規顧客作成用画面をワークスペースに表示



メイン画面の中が左右2つに分かれています。右側には顧客情報の参照や編集のためのワークスペースが存在します。これはVisual Studioのエディタ領域をイメージすると分かりやすいかと思います。一方、左側には顧客リストを表示するためのリンクと、新規顧客作成用画面を表示するためのリンクが存在しています。これらのリンクを押下することでワークスペースに顧客リスト、あるいは新規顧客作成画面がタブとして表示されます。

この画面のViewModelの構造をクラス図で見てみます。(MSDN Manazineにもクラス図はありますが、ViewModelの全体の構造が分かりにくいため、サンプルのソースコードから改めて私の方でクラス図を書き起こしました。)



ViewModelBaseがすべてのViewModelの共通親クラスとして存在しています。

WorkspaceViewModelはCloseCommandプロパティを持ち、クローズ可能なViewModelとしての意味合いを持っています。それらのサブクラスとして、新規顧客作成のためのCustomerViewModelと、顧客リストのためのAllCustomerViewModelと、メイン画面のためのMainWindowViewModelがあります。
(なお、個人的にはWorkspaceViewModelのサブクラスは、ワークスペースのタブに含まれるものだけにして、MainWindowViewModelを含めないほうが良かったのでは、と思います)

メイン画面を表すMainWindowViewModelを見てみると、Workspaces関連に自身がタブとして持つWorkspaceViewModelのリストを保持しています。また、Commands関連に顧客リストを表示するためのリンクと、新規顧客作成用画面を表示するためのリンクを表現するCommnadViewModelをリストとして保持しています。

このように継承関係やコンポジション関連により、ViewModel全体がしっかりとした構造を持ち、連携が行われていることが読み取れます。

これは比較的単純な画面の例ではありますが、複雑な画面になれば、構造もそれに応じて大きくなっていきます。


□画面構造にViewModel構造は依存する

このサンプルプログラムで確認しておきたいのが、画面構造とViewModel構造の対応関係です。メイン画面の左側のリンク群と右側のワークスペースのタブ群の構成に対応して、MainWindowViewModelにリンク群を表現するCommands関連と、ワークスペース群を表現するWorkspaces関連が存在します。

このように画面構造(画面の概念的な構造)とViewModelの構造とが対応づいているのは偶然ではありません。これは明に暗に画面のモデリングの結果としてViewModelが定義されているからです。そしてViewModel設計のインプットとして画面仕様が必要であり、またViewModel構造は画面仕様(画面構造)に依存するということでもあります。


□ViewModel設計のための画面仕様は、まずはホワイトボードレベルで構わない

ViewModel設計に先だって画面仕様が決まっていなければならないことを説明しました。

このViewModel設計のための画面仕様は、まずはホワイトボードに記述する大雑把なレベルのもので構いません(もちろん詳細に決まっていて困るものでもありませんが)。これは、ViewModelは段階的に詳細化していけば良いためです。

ドメインモデルの設計でも、細かな振る舞いはまずはおいておいて、大雑把な構造を洗い出すことを優先することが多いのではないでしょうか。同様にViewModelの設計でも、まずは大雑把な構造を洗い出すことを優先したほうがうまくいきやすいように思います。実際のところ、よほど簡単な画面でもない限りViewModel設計をはじめから完璧に仕上げるのは不可能です。

特にViewModelでは、画面のための細かな属性や操作が多くなり、またドメインモデルよりも実装寄りの設計となる傾向があります。このため、ViewModel設計で細部に注目してしまうと全体像がおろそかになって袋小路に入り込みやすいのです。属性や操作の細かな 洗い出しはまずはおいていて、箱と線による構造の洗い出しに注力するようにしてください。細かな属性や操作はViewModelを実装しながら洗い出して行っても良いと思います。

画面仕様もViewModelもそれぞれ段階的に肉付けしていけば良いのです。


□[余談]WPFからSilverlightへの移行は容易か

ところで話が逸れますが、MVVMで開発した場合、WPFからSilverlightへの移行のように、UIシステムの変更が可能だといわれていま す。これは、WPFもSilverlightもViewに属するので、UIシステムの変更でViewは変わっても、ViewModelはそのまま再利用可 能であるということを指しており、実際その通りではあるのですが注意が必要です。というのもViewModelの構造は画面仕様に依存します。このため、 ViewModelを変更せずに済むのは、画面仕様を変更しない(あるいはほとんど変更しない)で移行した場合の話だということです。

も しも、WPFからSilverlightへの移行が、PC画面での使用からモバイルでの使用を意図したものであるとすると、画面サイズの制約により画面仕 様が根底から異なるものになるでしょう。こうした場合には、ViewModelを大きく修正する必要があり、流用可能なViewModelのコードはおそ らく半分もありません。むしろPC画面用とモバイル用のViewModelの無理な共通化は、ViewModelを複雑怪奇な構成にしてしまう可能性があ ります。そういったことをするよりはViewModelを最小限の共通化にとどめてイチから別途開発ししつつ、共通化はModelのレイヤで目指したほう が良いことも多いと思います。

個人的には、MVVMによるUIシステムの変更容易性のメリットは、少し過大評価されているように思います。

私は以前、ViewModelによってWPFからSilverlightなどへのUIシステムの変更に強いことから、「ViewModelは画面仕様の変更にも強くなければならず、画面仕様から独立した存在としなければならないのではないか」と勘違いしていたことがあります。しかし、このような 勘違いは、ViewModelの存在を、どのような画面にでも使用できるような、単なるModelラッパーにまで責務の範囲を狭め、有効な ViewModelを設計できない原因となります。変な表現ではありますが「むしろ安心してViewModelを画面仕様に依存させてください」。

なお、ViewModelは画面仕様に依存するのですが、ViewModelがあることで画面の変更容易性が下がるという意味ではないことに注意してくださ い。実際のところ、ViewModelは画面のモデルであり、抽象化が行われる分だけ画面の修正には強くなります。しかしそうはいっても、画面構造の大き な変化には、ViewModelであっても大きな影響を受けるということです。

(なお、ViewModelにも画面 仕様よりのViewModelと、モデルよりのViewModelがあり、このため画面仕様の変更のおよぶViewModelと、変更の及ばない ViewModelがあります。この辺りについては、機会があればご説明したいと思います。)


□ViewModelを検討してから、Viewを実装するという流れを守る

前回のブログの「期限が迫っている項目を赤く強調表示したい」というストーリーにて、ViewModelによる抽象化された画面に対するプログラミングの実現と、ViewModelとViewの主従関係について説明しました。このように画面の制御はViewModelに対して行われ、Viewがそれを実現するという関係があります。

コンポーネントに例えるとViewModelがインタフェースであり、Viewはその実装といっても良いでしょう(※1)。こうして考えると、手順としてインタフェースであるViewModelを検討してから、実装であるViewを検討するというのはごく自然な流れです。この検討の流れは必ず守るようにしてください。

この流れを守ることでViewが実現すべき目標をViewModelが与えてくれるようになります。Viewは、ViewModelが意図する仕様に向けて実装すれば良いのです。明確な目標が存在することで高い生産性を保ちつつViewのプログラミングをすることが可能です。

一方、View側から着手してViewModelを作成していくスタイルの開発を進めていくと、ViewとViewModelの境界を探り探りプログラミングすることとなり、自信を持ってプログラミングすることができません。作成されるViewModelも、他のViewModelと連携が行われない孤島のようなViewModelとなりやすく、結果としてViewModelとしての力を発揮することができません。 こうなってしまうとViewModelを導入せずににコードビハインドで実現したほうが、むしろ手数が少なく幾分かマシという状況にさえなってしまいます。

こうしたView側から着手してしまう状況は、幸か不幸かVisual StudioやExpression Blendで画面仕様の定義を兼ねて簡単にXamlでスケッチをかけことにより、比較的容易に発生します。スケッチとして作成したXamlをそのままViewへと転用することで、ViewModelの設計がなおざりになってしまうのです。こうしたスケッチは、そのままMVVMのViewとして使用するのではなく、 ViewModelの設計を行ったうえで、使用できる部分だけ利用する程度に留めることをお勧めします。基本的にはそうしたスケッチは すべて捨てるぐらいの覚悟で丁度良いと思います。

ViewModelを検討してから、Viewを実装するという流れを守るようにしてください。

※1:一般的なインタフェースと違って、ViewModelは単体として意味を持つものではありません。複数のViewModelインスタンスが、 オブジェクトツリー(あるいはグラフ)となり、その構成全体として画面の状態を表現します。こうした点は異なるものの、ViewModelとViewには 主従関係があり、イメージとしてViewModelがインタフェース、Viewはその実装と考えても問題ないと思います。


[原則2]画面定義後、Viewの実装の前にViewModelの(簡易)設計を行うこと

今ままでの説明が、画面仕様の定義後にViewModel設計を行うこと、またViewModel設計の後にViewの実装を行うべきであるということの解説となります。

Viewを実現するWPF/Silverlightや、MVVMフレームワークなど、これら自体が大きな技術ですので、圧倒されてしまって結果的にViewを開発の中心に据えてしまう気持ちも分からないではありません。しかし、これらはあくまで実現手段にすぎません。

そうではなくViewModelを中心に据えるようにしてください、、、と、なんと実はこちらも間違いです。

「本当の意味で中心に据えるべきはユーザであり、そこから段階的に詳細化しながら導かれていくのが、システム仕様であり、機能仕様であり、画面仕様であり、画面設計であり、ViewModelであり、Viewの実装なのです」。むしろこちらの方がソフトウェア開発全体を捉えたより正しい原則といえるかもしれません。

ViewModelさえもあくまで画面の実現手段にすぎません。私自身よく忘れてしまうことですが、技術に振り回されずに、より大きな目的の中に技術を位置づけることが大切です。

[原則2]画面定義後、Viewの実装の前にViewModelの(簡易)設計を行うこと

これで、今回のブログは終了です。長文お疲れ様でした。

以上。

人気の投稿