【Unity】クラスは膨らむ前に分けよう。保守性と可読性の向上へ【C#】
Unityでコーディングする際は、意識していようといまいとC#のクラスをたくさん作成しています。
例えば「SceneManager」や「PlayerController」といったクラスが作られるかと思います。
こうしたクラスにはついつい機能を足してしまってクラスの内容が膨らみがち。そこで、ある程度プログラミングに慣れてきたらクラスを分割してみましょう。
クラスは膨らむ前に分けよう
Unityのチュートリアルだと、ひとつのクラスにどんどん機能を追加していくことが多いです。これは説明の内容をシンプルに伝えようとすると、クラスの分割やらの話を盛り込むのは得策ではないためです。特に初心者の時にクラス設計の話をされてもいきなり理解するのは難しいですからね。クラス設計の話は大切なことではありますが、チュートリアルでは優先順位を考えて敢えて触れていなかったりします。
こうしたチュートリアルを通してUnityを使ってゲームを作り始めると、ついついひとつのクラスにいろんな機能を持たせちゃったりするかもしれません。というか私も最初はついついやっちゃってました。
クラスが大きくなると内容を把握するのに時間がかかりますし、スクロールして目的のメソッドを探すのにも時間がかかります。検索すれば一発ですが、全体の見通しという意味ではちょっと大変です。
そこで、クラスは膨らむ前に適切なサイズに分割しておきましょう。
すでにあるクラスを分割することも大切です。それに加えて、できれば設計段階から役割や機能に応じてクラスを分けておくとコーディングがしやすくなります。
クラスごとにどのように関わりがあるかも事前に決めておくとグッド。UMLだとクラス図を作っておくイメージですね。UMLについては簡単な概要を以下の記事で紹介しているので、書籍などで調べていくための取っ掛かりにしてもらえればと思います。
神が生まれる日
「動けばいいや!」という観点だと、究極的にはクラスは1つでも実装できます。
ゲーム内の全ての状態、全ての機能を把握している全知全能の神のようなクラスを作ればゲームを動かすことができます。(いわゆる「神クラス」) ※これはエンジンコードは別として、Unityを使って開発する私たちがスクリプトを1つ作ってそこに機能を詰め込めばゲームを動かせる、という意味です。
ただ、コードは人間が管理しないといけません。バグが発生したら修正する必要がありますし、パフォーマンスを改善したいことだってあります。
神クラスがあると、こうした要望が生まれた時にとてもじゃないけど内容を把握しづらいんです。
例えばコンソールで「26485行目でnull参照があります」と表示されていても、まずその行にたどり着くのが苦行です。
このメソッドを呼んでいるのは15384行目のメソッドで……このメソッドではクラスの先頭に書いたフィールドを見ていて……なんてとてもじゃないけど追いきれませんよね。
そのゲームを完成させてすぐの頃は内容を把握できていても、3ヶ月後にレビューでエラー報告があって直そうとしたら目の前にはもはやロゼッタストーンの如く解読が困難なクラスが立ちはだかっていて、残念! 私の修正はここで終わってしまった! となりかねません。いや、実際にはそのエラーは泣きながらでも直すんですけど(笑)
というわけで、人間が管理しやすいようにクラスは役割や機能ごとにうまく分割しましょう。
分割の方針
分割しすぎると「かみはバラバラになった」のごとく、繋ぎ合わせるのが大変になるので適切な単位でクラスを設計するのがベスト。
……まぁこの「適切な単位」が難しいのですが、大体の目安としては、
・1000行を超えたら神クラス疑惑
・100から300行くらいを目指そう
・なんならもっと短くてもいい
という感じでしょうか。これはあくまでコードの長さの観点からの分割なので、他にも機能面や役割の面から考えていく必要があります。
SOLID原則
例えばSOLID原則と呼ばれるソフトウェアを分かりやすく、保守しやすくするために考えられた原則があります。これは5つの原則の頭文字をつなぎ合わせた言葉になっていて、
- Single Responsibility principle (単一責任の原則)
- Open-closed principle (オープン/クローズドの原則)
- Liskov substitution principle (リスコフの置換原則)
- Interface segregation principle (インタフェース分離の原則)
- Dependency inversion principle (依存性逆転の原則)
という5つの原則を意識してコーディングすることで分かりやすく管理しやすいコードを目指すものです。特にクラス設計においてはSの単一責任の原則が大切です。
単一責任の原則とは、
「変更する理由が同じものは集める、変更する理由が違うものは分ける。」
というもの。(『プログラマが知るべき97のこと』より)
例えばひとつのクラスの中に、主人公のパラメータや、移動時の速度などをまとめて持っていたとします。戦闘システムと移動システムが別々に実装されるRPGを想定した時に、戦闘システムでパラメータの変更があった場合にこのクラスを変更する必要があります。また、移動システムでも変更があった場合にもこのクラスを変更する必要があります。
この場合だと、変更する理由は戦闘システム、移動システムの2つが存在するのに対し、ひとつのクラスの中にこれらに対応するフィールドやメソッドが含まれていることになります。これの何が困るかって、変更後にテストする範囲(影響範囲)が大きくなっちゃうんですよね。
戦闘システムに関してパラメータを変更した時、同じクラスの中に移動システムに関する処理が含まれていることで、そちらに影響が出ていないことを確かめる必要があります。これは管理が大変になってしまう要因なので、クラスを分割して役割を分けることで管理しやすくすべきです。
KISS原則
SOLID原則のように頭文字をとった原則としてKISS原則があります。「Keep it simple, stupid!(シンプルにしておけ、このマヌケ!)」だったり、「Keep it short and simple.(短くシンプルにしておきましょう)」だったりしますが、要するにシンプルにすることを重点にして分かりやすくしようという原則です。
クラス分割もやりすぎるとクラスがやたら増えることになり、シンプルさが行方不明になってしまうため、バランスも大切だったりします。単一責任の原則に慣れてくると、「あ、ここ分けられるな」「こっちも別にしておこう」なんてどんどん細分化しちゃったりすることもあります。そうするとファイル数が肥大化してどこにどのスクリプトファイルがあったやら……と逆に大変になったりも。
ちょうどいいシンプルさを目指すのも大切なんです。
最初のうちはクラスにどんどん機能を追加していくことが多いかもしれないので、単一責任の原則を意識することでちょうどよくシンプルな感じになるかもしれません。
まとめ
Unityで開発しているとひとつのクラスにたくさんの機能を持たせてしまうこともあるので、適切にクラスを分割することを考えてみましょう。
このページで紹介した「SOLID原則」や「KISS原則」はいずれは知っておきたいところです。いや、「KISS原則」は今からでもいいです(笑)
ただ、最初から完璧を目指すとコーディングの手が止まるので、慣れてきてもっといいコードを書きたくなった時にクラスの分割や設計方法にも触れてみるといいと思います。まずは動くものを作るのがゲーム開発者の至上命題ですからね。いやほんと。
ゲーム開発の攻略チャートを作りました!
-
前の記事
UMLを知るとコーディングが楽になるので慣れてきたらぜひ【ゲーム開発】 2020.12.02
-
次の記事
【Unity】正しくエラーを制御して解決への手がかりを残す【C#】 2020.12.04
コメントを書く