#はじめに
Model View Controller Presenter ViewModel etc...
設計について調べると必ずと言っていいほどこれらの用語が出てきます。
今回はこの中で「Model」と「View」について根本的に理解することを目指しましょう。
Modelとは
フロントエンド・バックエンド・ゲームなど文脈によって定義や使われ方が異なる場合が多いです。
結果的に「データベースいじるやつ」とか「ビジネスロジック(Unityだとゲームロジック)」とか言われて、あいまいな理解になってしまうこともあるでしょう。
今回は「画面がなくてもゲームの本質として存在するような処理とデータのこと」として定義します。
具体的に考えてみましょう。
例えば、テトリスでは、「横一列にブロックが並んだら消す」とか「ランダムにテトリミノ(4つブロックがつながったアレ)を選ぶ」という処理は3DSで実装しようが、スマホゲームで実装しようが、Unityを使おうが、UE4を使おう、なんならコンソールアプリケーションとして実装しようが変わりません。このような処理をModelと呼びます。
また、「現在の盤面の状態」も同様に、どのプラットフォームで実装しようと変わらない画面に依存しないデータです。このようなデータもModelと呼びます。
具体的なアプリケーション実装においても、判断に迷った場合には「これ、ゲームエンジン移植しても変わらないコードになるかな?」と自問自答すれば良いと思います。
Viewとは
Viewに関しては、「見えるもの!」という分かりやすい基準があるのでそこまで認識にずれが生じるものではないかと思います。むしろ、Viewじゃないものを無理やりViewに抱え込んでViewが爆発することの方が多い気がします。
そこで、今回はあえて「見えるもの」ではなく、「Unityがないと絶対に動かせないもの」と定義したいと思います。
例えば、transformを操作したり、Monobehaviourを継承したりしている部分です。
これはなぜかというと、Unityがあまりに強力だからです。Webだと、根本的なデータは全部サーバーに存在するのでModelときっちり別れさせざるを得ないですし、同じC#でもWPFなどだと厳格にViewが区切られていて、他の部分からViewにアクセスする手段がバインディングを除くとあまりないです。(このあたりの話はUnityと協調するためのアーキテクチャ『MVP4U』という記事でも触れられているます。)
それに対して、Unityはその気になればすべてのロジックをMonobehaviourで完結させることが出来ます。Viewをいくらでも拡大解釈できるのです。したがって、Unityの文脈においては「Unityがないと絶対に動かせないもの」という定義がより適切だと考えられます。
ModelとViewを分離するとよい理由
1. 再利用性が上がる
Modelは、その定義から「どんな画面でも使えるデータと処理」の集合体です。これはつまり、全画面・シーンで使うことが出来るようなデータと処理がまとまった、と言うことです。もしも、ModelとViewが結合していた場合、Modelが使いたくても、Viewが抱き合わせでついてきてしまうので、Viewの持つ「特定の画面特有の他では絶対使えない処理」のせいで再利用が厳しくなってしまいます。
2. Modelのテストが容易になる
Viewから分離されたModelは純粋なC#のプログラムです。すなわち、簡単にユニットテストを書ける存在だということです。これは、コードを安定化させてくれます。テストのメリットについてはここ以外でも大量に語られているのであえて書くことはしません。
「テストなんて書かないよ!俺は趣味でUnityやってんだよ面倒なことやってられっか!」みたいな人にとっても価値のある存在です。というのも、テストが容易な設計それ自体が、周囲との依存関係を少なくして、コードの影響範囲を明確にした設計ということなので、最低限の品質を担保してくれるからです。
また、「テストを書いて仕様変更したら悲しくなるからやだ」みたいな人にとっても有用です。Viewの変化に比して、Modelの変更は圧倒的に起こりにくいです。Modelのコア部分にテストを書いておくことは効率のいい投資になります。
3. Unityの再生ボタンを押す回数が減る
Viewは実行コストが桁外れに大きいです。なぜなら、その定義から「Unityの再生ボタンを押して、実行を待って、ボタンを押して反応を見て、インスペクタから値をチェックして、ヒエラルキービューをチェックして…」といった手作業でのチェック回数が増えるからです。2.で触れたModelのテストの容易さとは異なり、相当苦労しないと自動化などが出来ません。ModelとViewを分離することでViewが小さくなり、煩わしい作業が減ります。
「Unityでいい設計をしたい場合には、Unityから離れてコードを書かないといけない」ということを覚えておきましょう。
( PureなC#を書くことの重要性についてはUnityにおける「設計レベル」を定義してみた という記事が参考になります)
4. 見た目を変更するコストが下がる
「ボタンの色を変更したらバグが発生した」とか「レイアウトを変えたら動かなくなった」とかの不具合による負担が減ります。
その理由は、「見た目のバグは絶対にViewに存在する」という確証が持てるからです。いままで動いていたということは、Modelにおかしな点はないということですから、見るべきコードの範囲が減ります。
また、「Viewが表示に徹すれば必然的にViewがシンプルになるのでバグを埋め込みにくく、不具合自体が減る」「Viewに依存したロジックを書くこと自体が出来なくなり、見た目の変更がバグを誘発する状況を作りにくくできる」といった理由から、見た目の変更、すなわちViewの柔軟性が上がります。
Viewの柔軟性が下がる原因のうち、「見た目変えたらボタンの位置や色に依存するロジックが壊れるからやりたくない」というものを完全に排除できます。
詳しくは、補足記事を参照してください。
5. 純粋なオブジェクト指向による開発が出来る
ショッキングな書き方をすれば、実はUnityは、純粋なオブジェクト指向プログラミングを破壊する存在と言っていいです。
Unityの根本的な設計思想は機能ごとにコンポーネントを作ってそれを組み合わせたゲームオブジェクトによってゲームを構成することにあります。Unityはコンポーネント指向のゲームエンジンなのです。
したがって、ユーザーが定義できるプログラムはUnity上に存在する限りはMonobehaviourに限られてしまいます。
C#では多重継承が許されていないので、ユーザーはMonobehaviourを継承する限りは、他のクラスを継承することはできず、オブジェクト指向の機能は制限されてしまいます。
これは、ソフトウェア開発の方針の違いであって、優劣がつくものではありません。しかし、すくなくともModelを開発する段階においては、コンポーネント指向が足かせに働くことも多くなってしまいます。詳しくは、補足記事を参照してください。
ModelとViewが分離した状況下では、ModelはUnityの存在を知らない(今回の定義によれば、using UnityEngineした瞬間にそれはViewとなる)ので、Modelは純粋なオブジェクト指向の技法を用いてプログラムを書くことが出来ます。
ソフトウェア工学の書籍で学んだ技法を生かそうとしても、「実際問題Unityだとそう書いたら動かんじゃんどうすんの!」みたいに嘆く必要はもうないのです。
ModelとViewの分離の達成度のチェック項目
- Modelが純粋なC#のみで構成されている
- ModelはUnityのことを全く意識していないつくりである。using UnityEngineしていない。また、「特定のボタンの表示可否」とか画面ありきの情報をもっていない
- Viewが十分に小さい。理想は、流し込まれたデータを一直線に処理して条件判断をしないViewであること
おわりに
ModelとViewを分離したほうがいいというのは、多くの記事で暗黙の了解として扱われがちな事項ですが、初めてMVXアーキテクチャに触れる際にはその理由やメリットをしらず、アーキテクチャ自体の正当性に疑問を持ってしまいがちです。MVCでもMVVMでもMVPでも、ModelとViewは共通の存在で、もっとも重要な概念と言っても過言ではありません。しっかりと腹落ちさせておきましょう。