前説
**「ロジックだけをUnityから分離することは可能か。可能だとしてどこまでできるのか」**という個人的興味から、UnityのScriptの構成について、サンプルコードを用いて手を動かしながら考察してみた。
本記事はその推移を振り返りをかねてまとめたものである。
過去記事
― UnityのScript構成を考えてみる
― UnityのScript構成を考えてみる その2
― UnityのScript構成を考えてみる その3
― UnityのScript構成を考えてみる その4
― UnityのScript構成を考えてみる その5
1. View のみ、共通化なし
まずすべての処理が一クラスに混然と入っている状態からスタートした。あえて共通化を行っていないため(※)、保守性の低さが目に付いた。
※ 実際にこのようなクラスはコピペが多用され共通化があまり進まない傾向がある。
2. LogicとViewの分離
クラス図補注: Configは共通クラス(Utility)のため逐一線を引いていないが、LogicがConfigを利用している。
3. Observerパターン導入によるLogicとViewの疎結合化
しかしViewクラスが結果を直接受け取る方式であったため、Logicが呼び出し元を意識する必要が残っていた。また、今回は問題とならなかったが、Logic側から更新を発信することも困難だった。
そこでObserverパターンを導入し、LogicとViewの疎結合化を進めるとともにLogic側からイベントを起こすことを可能にした。
実はこの時点ですでにViewクラスはViewModelと呼んだ方が良さそうな状態になっている。値を保持し、ロジックを監視する、表示部分の原本である。
が、後でUniRxを導入したいと思っていたため、この時は触れずにおいた。
4. Logicクラスを切り直す。単一責任原則およびレイヤアーキテクチャを意識して。
一方、Logicクラス側は切り出したばかりで単一責任原則違反が目立ち、またロジックとデータアクセスが入り混じっていた。このような状態は変更に弱い。
そこで、単一責任原則、およびロジック部分とデータアクセス部分を分けることを意識し、Logicクラスを元にして新しくクラスを切り直した。
5. UniRxを導入し、ViewをPresenterに。
4までで止めても割と行けると思うが、今回はさらにUniRxを導入してMV(R)Pパターンに組み替えた。生のEventを使う場合と比べて書くのが楽になる他、Rxの概念を利用できるようになるのが主なメリットである。
とはいえ正直かなり大変だったので、やるなら気合を入れてかかったほうがよいと思う。
6. Factoryを利用したレイヤ間依存の疎結合化
最後にFactoryを使い、抽象に依存するようにしてレイヤ間依存の疎結合化を図った。
後で気が付いたが、Stairwayパターンと呼ばれる状態に近くなっている。
主な目的は
- Model部分を切り出してコードを流用できるようにしたい
- テスタビリティを向上させたい
- 開発中にスタブを使えるようにしたい
といったところである。
が、この手の予定がない場合は導入コストに見合わないためやめたほうがよいと思う。
最終形の説明
- 最終形はMV(R)Pと呼ばれる構成になっている。図の中ではそれぞれVがUnity、(R)PはPresenter、MがNumberMediatorをはじめとする青色の背景色をしたクラスに対応する。最初から絶対にMV(R)Pの形にしようと思って考察を進めたわけではないのだが、表示とロジックの分離、疎結合化、UniRxの導入を行ったら結局こうなった。
- また、Model部分はビジネスロジック層とデータアクセス層の二つにレイヤを分けている。図で言うとNumberMediatorとNumberがビジネスロジック層、NumberFileがデータアクセス層にあたる。
- レイヤアーキテクチャの観点で見ると、View層-Presenter層-ビジネスロジック層-データアクセス層の4層構造となる。
- 図ではFileクラスとなっているが、あるライブラリを導入しようとしたらバグが出たため途中でDataFileと名を変えた。そのためサンプルコードではDataFileとなっている。(ごめんなさい)名前空間をサボらずに使うべきだった。しかもそのライブラリは結局使わなかった。
サンプルコード
利用したサンプルコードはGithubの下記URLにアップした。
https://github.com/nakatatsu/unity-sample
結論
- 表示とロジックの分離、疎結合化、UniRxの導入を行うと、結局MV(R)Pに行きついた。最初からMV(R)P構成で組んでよさそうである。
- もちろん別の構成もありえると思うが、結局は似たような構成になる気がする(UniRxを導入しないなら話は別だが)。
-
MV(R)P構成でいうところのModel部分は、Unityからの分離が可能である。
- UnityEngineとMonoBehaviourの利用を禁止すれば、と前置きがつくが、そう難しいことではないだろう。
- 一方でPresenterの分離は難しい。
- MV(R)P構成または類似した構成をとる場合、Modelに書けるコードはなるべくModelに書くようにし、Presenterに書かないほうが、コードの流用やユニットテストの実行を行いやすくなるだろう。
- ViewはUnityべったりなので分離は無理。
以上。
本記事が何かのお役に立てましたら幸いです。