「電卓」アプリの構造
最後に、今回の機能追加はアプリの構造のどの部分に行ったのかを、振り返っておきましょう。前回で説明したように、全体の構成はMVVMパターンになっています(次の画像)。その中のどこを修正したのでしょうか?
始めの作業は、画面に表示する「坪」という文字列をResources.reswに追加することでした(下図の①)。これはCalculatorプロジェクトの配下なので、Viewです。ただしクラスとは違って、Viewにあるとはいっても、参照関係にないViewModel/Modelの中からも参照できます。
次に、「坪」を表す定数Area_TsuboをUnitConverterDataConstants.hに追加しました(同②)。これはCalcViewModelプロジェクトの中にあるので、ViewModelです。
それから、「坪」の文字列リソースを読み込むコードを、UnitConverterDataLoader.cppに追加しました(同③)。これもViewModelです。
最後に、「平方メートル」と「坪」の換算率を、同じくUnitConverterDataLoader.cppに追加しました(同④)。これまたViewModelです。
なお、実際に換算するロジックはUnitConverter.cppにありました(同⑤)。これはCalcManagerにありますから、Modelです。
この図を見て、「ViewModelに偏りすぎているのでは?」と思いませんか。④換算率は、表示するわけではないので、Modelだけが知っていればよいものです。それなのに、換算率を必要としないはずのViewModelが保持していて、Modelに換算率を与えています。また、換算率を結びつける②定数Area_Tsuboは、選択肢の識別のためにViewModelでも必要とされますが、Modelに定義しておいてViewModelに対して公開すればすむことです。理想を言えば②と④はViewModelではなくてModelにあるべきでしょう。しかし、そのような構造にしてしまうと、初期化処理がややこしくなるでしょうし、今回のような機能追加のときにもきっと面倒な作業になってしまうでしょう。
今回の機能追加は、ViewModelに大部分の変更箇所がまとまっていたおかげで、楽にできました。デザインパターンの理想を追うのか、それとも作りやすさ/変更しやすさを優先するのかというのは悩ましいものですが、1つの参考になると思います。
③文字列リソースの読み込みはModelにあるべきか?
MVVMパターンの理想を追う場合、上図③の文字列リソース読み込みもModelに置くべきだと思われるかもしれません(表示には関係のない処理ですから)。しかし、文字列リソースの読み込みはプラットフォーム依存の処理なので、「Modelはプラットフォーム非依存であるべき」という考え方をするなら、Modelに置くことは否定されます。
実際に文字列リソースを読み込むコードは、ViewModelのCommon/AppResourceProvider.cppにあります(GetResourceStringメソッド)。そこで文字列リソースの取得に使っているのは、UWP APIのWindows.ApplicationModel.Resources.ResourceLoaderクラスです。このコードをModelに置いてしまうと、UWP以外のプラットフォーム(例えば、WPFや、XamarinのAndroidやiOSなど)では使えなくなってしまいます。プラットフォーム依存を避けてこれを解決するには、Dependency Injection(依存関係の挿入)を利用します。
