LoginSignup
7
2

More than 1 year has passed since last update.

Unity設計入門:第2章「MVXの思想」理論編その1

Last updated at Posted at 2021-10-22

Unity設計入門の目次はこちら


はじめに

前回、ModelとViewがそれぞれ何者であるかに加えて、それらを分離することの利点について書きました。
しかし、現実問題としてModelとViewを分離することには大きな困難を伴います。
今回は、なぜModelとViewがキレイに分離できないのか、具体的な原因の特定を目指します。

ModelとViewが分離できない状況

1. 表示制御のためのデータが増加する

どのようなデータが「表示制御のための一時データ」に該当するか

複雑なゲーム画面を組み立てていくと、ModelでもViewでもないデータが増加します。
これらのデータは「サンプルコードではシンプルすぎて出てこないので解説されにくいけれど、現実的にゲームを組むとどうしても出てきて扱いに困る」という厄介な存在です。
これらのうち、具体的には以下のようなものを「表示制御のための一時データ」と呼ぶこととします。

  • アニメーションの秒数を管理するためのフラグ
  • マウスを早く動かしたときにのみ動かす演出のために、マウス座標の差分をとっておくための変数
  • スナップするスクロールビューにおいて、現在どこのページにいるかを保存しておく変数
  • UIの入力モードが複数あるときに、そのどれであるのかを保存しておく変数

このようなデータは、業務用のソフトウェアでは無視できるほど少ないでしょう。ほとんどが演出用であり、省いてしまえばよいのです。しかし、ゲームは演出です。これらの変数が莫大に増加することがほとんどなのです。
さて、これらのデータをどこに記述していくのかについて考えるとなんとModel・Viewのどちらにも書けないということが分かります。

Model側に記録した場合の問題点

Modelにアニメーション状況や表示可否などの情報を記憶させる方式です。
Modelが画面に依存してしまいます。
Modelが画面のレイアウト・演出に合わせて変更に迫られるということです。
これはViewの柔軟性を損なうとともに、Modelの一部のテストにViewが要る状況を生んでしまいます。
これは前回の原則「Modelは純粋にする」に反するコードです。

View側に記録した場合の問題点

Modelから受け取った情報とViewが持っている情報を合わせて演出を出す方式です。
Viewに条件判断が増え、肥大化します。
Unityの起動回数は増え、面倒なテストを何度もする羽目になります。
これは前回の原則「Viewは極限までシンプルにする」に反するコードです。

2. シンプルなViewから受け取る入力は低レベルすぎる

ユーザー入力はUnity、すなわちViewを通して受け取らざるを得ません。そしてユーザーはViewを通してModelを操作しようとするのです。小さなサンプルコードだけでは前回のように「ボタンを押して、画面に表示してハッピーだね!」というようになって終わりなのですが、ユーザーの操作は

  • ダブルクリック判定をするために、「直前15フレーム以内にクリックがあったか」を保存しておく変数
  • 3回のボタンクリックをすることで初めて意味を持つ入力
  • カードをドラッグアンドドロップして別の場所に置くときの入力

など多岐にわたります。そしてこれらの入力は、意味を持つタイミングが「複数のデータ・一次変数から複合的に判断される」という特徴を持ちます。これはボタンによる入力が「押された瞬間に意味が確定する」ものであったこととは対照的です。
さて、それでは上に挙げた例の「直前15フレームの入力の有無」「ボタンクリックが現在何回されているか」「ドラッグ中か否か」などの情報はどこに保存すればいいのでしょうか?
じつはこれもModel・Viewどちらにも書けないのです。

Model側に記録した場合の問題点

Modelが「画面の(x,y)がクリックされたときに呼ぶ関数」などを用意して、低レベル情報を直接受け取る方式です。
この場合、同じくModelがViewを気にしてしまうためModelの純粋性を損ないます。
さらに、この手の関数をテストしようとするとテストケースに「(23,455)をクリックしたときの関数を呼ぶ→マウスの座標(22,444)に移動したときの関数を呼ぶ」と言ったように非常に低レベルなものになります。この(23,455)とかの座標は結局Viewを見ないとダメですし、こんなテストを書くぐらいなら、Viewを起動して動作確認するほうがよっぽど早いですね。つまりこれは、「Modelがテストを書くことを容易にする」というメリットを完全に破壊しているのです。

View側に記録した場合の問題点

Viewが「どこをクリックされたか」などの情報をためておき、ある程度たまったタイミングで、画面から離れた抽象的な意味を持つUI操作としてModelの関数を呼び出す方式です。
この場合、Viewが大きくなり、「Modelの関数がちゃんと呼ばれるか」の検証が非常に面倒です。
Viewはシンプルにするという原則に反するコードです。

3. 「ModelとViewの責務」という言葉を曖昧に使っている

これは1,2とは異なり、構造的にどうしようもないもの、といえるものではありません。
Viewを見た目だ!ぐらいのぼんやりしたイメージ、Modelをデータの入出力だ!ぐらいのぼんやりしたイメージで組み立てると、往々にしてViewがModelの処理を奪い取ります。(ModelがViewを奪うことはあまりありません。というのも、Viewは最小限の状況が一番いい状況だからです)
また、View・Modelの2人体制だと、ViewはModelを参照できるわけですから、Modelをpublic変数から好き勝手操作できてしまいます。
結果としてModelとViewの密結合を生みやすくなります。
ModelとViewの原則、「純粋性」と「シンプルさ」を強く意識したコーディングである程度は防げますが、根本的にModelへの参照をなくしたり、疎結合にしたりする策を講じないとView側でいじってしまうという問題はなくなりません。

おわりに

経験的には、「サンプルコードで書けてるようにキレイにならない!!なんで!!」という叫びの原因のうち、大部分はここにあると思います。また、漠然とViewとModelとX(ControllerなりPresenterなりViewModelなり)を切り分けたいというだけの思考でMVXの設計をするのではなく、「どうしようもないからXへ持っていく。ModelとViewは明確な原則に従って設計する」の方がよい設計になることが多いと思います。次回、今回上げた問題の解決策を探していきます。

7
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2