Prism/XamarinでAndroidアプリの作成(2):お前がBlank Appの中身をみるとき、Black Appもまた以下略
ViewとViewModel
ソリューションエクスプローラーで、作ったテンプレートで作ったBlankAppの中を見るとこんな感じ。なんだかViews,ViewModelsというフォルダができている。ViewsにはView、ViewModelsにはViewModelが置かれる。
うん、さっぱり説明になってないな。
PrismはMVVMというアーキテクチャを採用している。
MVVMについてはググればいろんなところにちゃんとした説明が書いてあるし、ここで私が胡乱な理解で怪しい説明をしても害にならないだろうから省く。大雑把に言えば、見た目と処理を分離して、Viewに見た目、ViewModelに処理を置くソフトウェアアーキテクチャだ。そのViewとViewModelはたがいに依存せずに疎結合にすることによって、見た目と処理を構造的に分離しているのがMVVMの大事なところ。
例えばある機能をUIで提供するときに、その機能をボタンで提供するのか、トグルスイッチで提供するのか、はたまたほかの方法なのか、そのボタンは画面上のどこの位置にあってどういう色で表示するのかみたいなことは全部Viewでやって、そのボタンが押された時の処理はViewModelでやりますよということになる。
ちなみにMVVMの2文字目のVが”View”で、3-4文字目ののVMが”ViewModel”の意味。じゃあ最初のMって何ってことに当然なるんだが、これは”Model”の意味。Viewが画面、ViewModelが画面裏で動く処理、Modelはさらにその裏側にあるビジネスロジック本体という構成になる。
ViewModelとModelはどうちがうのか。これは実践的にはかなり難しい問題のような気がするが、私はソフト本来の処理というのはModelにいて、ViewModelはModelのフロントエンドみたいな役割をするような作りがいいのかなあと勝手に思っている。(”UIは本来の動作じゃないのかよ”という意見が当然聞こえてくるが、そんなものはもちろんソフト本来の動作じゃない。なんで計算機様が人間に気を使わないといけないのだ!)
※筆者は携帯電話のドライバを開発していた時代にUIソフト開発担当にすごくいじめられた覚えがあるので、UIを憎んでいます。坊主が憎いと今朝まで憎いんです。それが人間だから。
繰り返しになるが、これは私の胡乱でかつ自分の設計思想をMVVMに乗せたいがためのあるる意味恣意的な理解なので、MVVMについてちゃんと知りたければ調べてほしい。ちょっとググればまともな解説は山ほど出てくる。
大雑把な理解としては、 これまでに書いた通り、UIのレイアウトをViewに書いて、動作をViewModelに書くというイメージでとりあえずはいい。
さて、アプリの中身に戻ろう。
MainPageというページに対して、そのViewModelがMainPageViewModelで、ViewがMainPageViewとなっている。
たとえばボタンを追加するなら、ボタンの配置をViewsの下にあるMainPage.xamlに書いて、ボタンをタップした時の動作をVieModelsの下にあるMainPageViewModel.csに書くというようなイメージ。
ViewModelBaseとはなにかというと、これはViewModelの基底クラスで、これを継承して各画面のViewModelのクラスを作っていくようになっているが、コードを書いていくにあたっては、今はとりあえずあまり気にしなくていい。
ブランクアプリの中身
テンプレートウィザードが吐くスケルトンアプリの解説なんて探せばどこでもやってると思うけど、一応ここでも中身を見てみる。
MainPage.xmal
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SamplePrismApp.Views.MainPage"
Title="{Binding Title}"><StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<Label Text="Welcome to Xamarin Forms and Prism!" />
</StackLayout></ContentPage>
細かい文法の解説は割愛するが、ここで説明したいのは赤字になっている以下のところ。
Title="{Binding Title}"
これを
Title={Title}
と書くと、このページのタイトルはTitleという変数ですよという意味になる。この場合、Titleは、(x:Classとして指定されている)MainPage.xaml.csにあるMainPageクラスのメンバとして定義されることになる。
しかし、MVVMの考え方では、Titleという変数の定義はViewであるMainPageクラスではなく、ViewModelであるMainPageViewModelクラスのほうで記載したい。
それを可能にするのが、このBindingという句である。これはデータバインディングという仕組みを示しているが、まあ名前は今はいい。コードを書く上では、Binding句を付けることによって、変数がViewのメンバではなくViewModelのメンバに定義できるんだと思っておけばいいだろう。(正直私も当面のところそれ以上の理解は放棄している…)
コードビハインドはこんな感じ。あっさりしたもんである。
MainPage.xaml.cs
namespace SamplePrismApp.Views
{
public partial class MainPage
{
public MainPage()
{
InitializeComponent();
}
}
}
TitleのTの字も出てこない。残念それはViewModelのほうにいる。
特殊なことをしようとおもわない限り、データの処理はViewModelde行い、UIの配置はxamlファイルで完結するので、このクラスには手を加える必要はない。
MainPageViewModel.cs
namespace SamplePrismApp.ViewModels
{
public class MainPageViewModel : ViewModelBase
{
public MainPageViewModel(INavigationService navigationService)
: base(navigationService)
{
Title = "Main Page";
}
}
}
using句は省略。
これもスケルトンなのでほぼ空っぽ。コンストラクタの中に1行処理が書かれているだけ。だがしかし、この1行がさっきのBinding句とペアになっている大事な処理だ。
Binding句を付けることによって、TitleがViewのメンバではなくViewModelのメンバだと宣言されたわけだから、ここに初期化が来るのは道理である。
Titleの初期化があるだけなので、これをViewでやったら何が悪いのかというのはぴんと来ないかもしれないが、Titleのテキストを何かの条件で書き換えるというロジックがあるとすると、TitleがViewのメンバにいた場合、Viewに処理を書くか、または、ViewのメンバをViewModelから書き換えるためにViewModelがViewのメンバを知る(Viewに依存する)必要が出てきてしまう。TitleをViewModelのメンバにすることにより、このいずれも回避し、ViewModelがViewに依存することなく疎結合を維持したまま、ViewModelだけでTitleの中身を変更する処理を書くことができるようになるわけだ。
これで、デザインだけ変えたい人はViewだけいじればいいし、データ構造作りたい人はデザインで悩みたい人に引っ張られることなく中身をガンガン作ることになる。
中身を一生懸命作ってる人がUIデザインやってる人の思い付きの「ちょっとここのボタンの色変えたいのでコード変えたから、マージしておいてね」みたいなことで煩わされる必要がなくなるのだ(※筆者はUIやってる人に含むところはありません。ないったらないです。/もちろん逆もまた真です。)
私はなんせUIのデザインに興味がない人間なので、処理を書くのにわざわざ画面の仕様変更に振り回されたくないのである。
まあ、これはあくまで僕の理解であり僕がMVVMを使う理由なので、みんなはみんなの理由でMVVMやPrismを好きになればいいと思う。
(私がPrismを好き?まさか…(ツンデレ))
若干蛇足気味の御託を並べてしまったが、ようやく次からコードを触っていこう。