Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unityにおける設計パターン

E97091ac0d2c69ce9f9e8a8d0a2ea066?s=47 torisoup
February 19, 2021

 Unityにおける設計パターン

E97091ac0d2c69ce9f9e8a8d0a2ea066?s=128

torisoup

February 19, 2021
Tweet

Transcript

  1. Unityにおける 設計パターン 2021/2/19 とりすーぷ

  2. 自己紹介 •とりすーぷ • @toRisouP • VR系の開発してる • Microsoft MVP 2018~

    • Developer Technologies • 最近はVRChatしてます illustrations by kota(@kt_kkz)さん
  3. 本を出しました •「 UniRx/UniTask完全理解 より高度なUnity C#プログラミング」

  4. 今回の話 •設計・アーキテクチャとは •Unityにおける設計のレベル •設計レベルをあげるためには •クリーンアーキテクチャとは •Unityでクリーンアーキテクチャ

  5. 発表はショート版です •調子に乗って100枚超のスライド作っちゃった •10分に絶対に収まらないので かなり端折ったショート版で発表します! •公開する資料はフル版です

  6. 先に言っておくと •この本読んでください

  7. 設計・アーキテクチャとは

  8. 設計とアーキテクチャ •設計 • クラス設計、コンポーネントの構成、全体の構成 • 「何をどうやって作っていくか?」という指針

  9. 設計とアーキテクチャ •設計 • クラス設計、コンポーネントの構成、全体の構成 • 「何をどうやって作っていくか?」という指針 俯瞰して見た設計をアーキテクチャ と呼ぶ

  10. 例:家の建築 •設計 • 壁の位置、形状、配管、配線… •アーキテクチャ • 家全体の形、間取り、部屋のレイアウト…

  11. 設計とアーキテクチャ •設計とアーキテクチャは地続きである • 詳細な設計の連続がアーキテクチャを構築する

  12. 「設計・アーキテクチャ」の目的 •「システムの開発・保守・運用にかかるコス トを最小限にする」ことが目的

  13. コストを最小限にする •クソコードに振り回される時間を減らす •後の仕様変更にも柔軟(ソフト)に対応する

  14. よくある誤解:その1 「設計を捨てて、開発メンバーが思い思いのや り方で開発した方が効率いいんだけど」

  15. 解 •「設計を捨てる」で得られた開発速度は、 後に訪れる莫大な保守コストで帳消しになる • 超短期的には速度が出るが、後に必ず失速する • 長期的に見るなら「ちゃんと設計する」以外に 開発コストを下げる方法はない

  16. ダメなコードの例 •スパゲッティコード • オブジェクトとオブジェクトがどう関係するのかがわからない状態 • 影響範囲が謎でどこを触っていいかわからない状態 • ちょっとした機能変更にもすごく時間がかかる状態 •外部モジュールと一蓮托生した状態 •

    「生殺与奪の権を他人に握らせるな」状態 • 外部モジュール(ライブラリやフレームワーク)の機能に強く依存しすぎた結果、 外部モジュールの仕様変更に振り回されてしまうような状態
  17. コードは腐る •少しの油断でコードはすぐにダメになる • 割れ窓理論 • どんな状況においても「設計を守る」を強く意識して 開発に臨む必要がある

  18. よくある誤解:その2 •「巷にある◦◦アーキテクチャっていうのを 使えば万事OKなんでしょ?」

  19. 解 •NOです • そもそもアーキテクチャは自分で考えてDIYするもので あり、万能なテンプレートがあるわけではない • プロジェクトに応じて柔軟な設計が求められる

  20. よくある誤解:その3 •「設計とかアーキテクチャって、要件がガチ ガチに固まっている、ブレがないプロジェク トでしかまともに使えないでしょ?」

  21. 解 •認識が逆です • 「変化に柔軟に対応できるようにアーキテクチャ を作っていく」が正解 • 機能の変更・追加・破棄があるのはソフトウェアの宿命 • 変化を受け入れられるアーキテクチャを作る、が理想 (現実はメテオフォールなどの根本的なちゃぶ台返しもあり上手くいかないこともあるが、

    それは組織体制の問題であって開発者だけで改善できる話ではない)
  22. 言い方を変えると •「変更にかかるコストを最低限にする」を達成して いればそれでアーキテクチャとして正解 • 巷にある「◦◦アーキテクチャ」から外れてたり、 アーキテクチャに汚い部分があってもそれで上手く 開発が回っているならそれでOK (設計しなくていい、と言っているわけではない!)

  23. よくある誤解:その4 •「そもそもUnityって特殊なフレームワーク(FW) だと思うんだけど、設計ってやって意味あるの?」

  24. 解 •あります。 • FWに振り回されるのは別にUnityに限った話ではない • Unityだけが特別なわけではない • 「(様々な)FWとの上手な付き合い方」はソフトウェア開発の 一般的な問題 •

    その対策として「クリーンアーキテクチャ」などの ◦◦アーキテクチャが考案されてきた
  25. 設計・アーキテクチャ まとめ •設計・アーキテクチャを使う目的 • 開発・保守・運用にかかるコストを最低限にすること • プロダクトの価値を高め続けられる状態を維持する •アーキテクチャを死守するのが開発者の使命 • 安易に「設計を捨てる」という選択肢を取らない

  26. Unityにおける設計のレベル

  27. レベルわけ •Unityにおける「設計のレベル」を 自分が勝手に決めてみました • 異論・反論は受け付けます(むしろ議論したい)

  28. 図の意味 GameObject (with MonoBehaviour) C# Pure C# Package (Assembly Definition

    Files)
  29. レベル0:設計が存在しない • 好き勝手作られた状態 • GameObjectベタ置き • 神クラスがたくさん • 相互参照、循環参照、 密結合でぐちゃぐちゃな状態

  30. レベル1:制御フローが整理された状態 • 制御フローが整理された状態 • オブジェクトの役割が明確になった • 参照関係が一方向に • 機能ごとに関係性が整理されている •

    密結合なのはそのまま
  31. レベル2:制御フローと依存関係の分離 • 疎結合化が進んだ状態 • SOLID原則が意識されている状態 • 依存関係逆転を考えてインタフェースが 要所要所に利用される C# C#

    C#
  32. レベル 3:モジュール化が意識される • 機能ごとにモジュール化される • モジュールの安定性が考慮される • 機能の再利用性が考慮される • asmdefが切られたりする

    (コンポーネント、という呼び方もあるが UnityのComponentと被るのでここでは モジュールと呼びます) C# C# C#
  33. レベル 4:ピュアなC#を活用する • ピュアなC#がメインロジック化 • MonoBehaviourを使わない実装が増える C# C# C# C#

    C# ←Unity依存はここだけ
  34. レベル 5:アーキテクチャが意識される • 全体のアーキテクチャを考慮 して一貫した作りになる • 詳細設計のみではなく、 全体を俯瞰してみた設計 (アーキテクチャ)が 強く意識される状態

    • レイヤの概念が入ってくるなど C# C# C# C# ↓Unity依存はここだけ C# C# C# C#
  35. レベル分け 5:アーキテクチャが意識される 4:ピュアなC#が活用される 3:モジュール化が意識される 2:制御フローと依存関係が分離する 1:フローが整理される 0 : 設計なし

  36. レベル分け 5:アーキテクチャが意識される 4:ピュアなC#が活用される 3:モジュール化が意識される 2:制御フローと依存関係が分離する 1:フローが整理される 0 : 設計なし ハイリスク

    ローリターン どんな状況でも最低限達成したい できてればよりGood コストは高いが メリットも大きい
  37. 設計レベルをあげるメリット •長期開発における機能追加・保守・運用の コストが下がる • 機能追加や仕様変更に柔軟に対応できるようになる • 大人数で開発しても破綻しにくい体制が作れる

  38. 設計レベルをあげるデメリット •開発者に対する負担も上がる • 開発規模に見合わない設計レベルは逆に手間を増やす • 開発者に求められる設計スキルも高くなる •動作パフォーマンスが落ちる場合もある • 「DIが遅い」「GCアロケートが増える」 •

    チューニングのためベタ書きコードがどうしても必要になる
  39. トレードオフ ⇔ • 理想を語るなら「長期的な運用のしやすさ」に振りたいが… • 現実は様々な理由で妥協しなくてはいけないことが多い • 場面によってはレベル1~4の範囲で収まってても全然よい 短期的な開発のしやすさ 極限を求めるチューニング

    長期的な運用のしやすさ
  40. 大事なこと •チームがやりやすいレベルで止めてもよい • 難しいと感じたらレベルを下げるのもあり • ただし最低でも「レベル1」は維持したい •設計レベルをハイブリッドにしても良い • チューニングしたいところはUnityと密にする •

    Unityと関係ない部分はできるだけピュアC#で書く
  41. ハイブリッドなやり方 C# C# C# C# C# C# C# C# C#

    C# レベル2で 構成された領域 レベル5で 構成された領域 依存
  42. 設計レベル まとめ •Unityにおける設計をレベルわけしてみた • 勝手に決めたレベルなので異論反論は受け付ける •どのレベルで開発するかバランスをみるの大事 • 理想としてはレベル5を目指していきたいが、 現実はそこまでいけることは稀 •

    どこかでバランスを取る必要がある
  43. 設計レベルをあげるためには

  44. 設計レベルの壁を超えるためには? 5:アーキテクチャが意識される 4:ピュアなC#が活用される 3:モジュール化が意識される 2:制御フローと依存関係が分離する 1:フローが整理される 0 : 設計なし ここの壁を超えるためには?

  45. レベル0→レベル1 •制御フローの整理を意識しよう • 制御フローをキレイにすることが何よりも大事 • フローさえ整っていれば最低限スパゲッティコードは 回避ができる

  46. 制御フロー •「誰が」「誰に」命令をするのかの流れ • オブジェクトの責務、メッセージの流れ ぐちゃぐちゃなフローの例 整理されたフローの例

  47. 制御フローの整理 1. オブジェクトのもつ役割をハッキリさせる 2. オブジェクトごとの主従関係をハッキリさせる 3. 相互参照、循環参照を避けて データの流れをわかりやすくする これで レベル0

    → レベル1 に上がる
  48. レベル2以上を目指すなら •制御フローに実装が振り回されない対策 が必要になる • オブジェクトの制御フローをそのままコードに落とし込んでい るだけではダメ(密結合化してしまい柔軟性がなくなる)

  49. 学ぶべきもの •「設計原則」を学ぶべし • 設計原則は「制御フローを変えずにどうキレイに実装す るか?」というやり方をまとめたもの

  50. 設計原則 •SOLID原則 • SRP、OCP、LSP、ISP、DIP •コンポーネントの凝縮性の原則 • REP、CCP、CRP •コンポーネントの結合性の原則 • ADP、SDP、SAP

  51. 設計原則 •SOLID原則 • SRP、OCP、LSP、ISP、DIP •コンポーネントの凝縮性の原則 • REP、CCP、CRP •コンポーネントの結合性の原則 • ADP、SDP、SAP

    ←制御フローと実装の分離に必須(レベル2に必須)
  52. 設計原則 •SOLID原則 • SRP、OCP、LSP、ISP、DIP •コンポーネントの凝縮性の原則 • REP、CCP、CRP •コンポーネントの結合性の原則 • ADP、SDP、SAP

    ←より大局的な視点での設計の話 (レベル3以上に必須) ←より大局的な視点での設計の話 (レベル3以上に必須)
  53. 詳しくは •この本読んでください

  54. 必要なこと 5:アーキテクチャが意識される 4:ピュアなC#が活用される 3:モジュール化が意識される 2:制御フローと依存関係が分離する 1:フローが整理される 0 : 設計なし 制御フローを意識しよう

    SOLID原則を学ぼう コンポーネントの 凝縮性・結合性の原則 を学ぼう
  55. 一番大事なテクニック •「依存関係の逆転」 • 依存関係逆転の原則(DIP)を行うのに必須 • これができるだけでもかなり違うので絶対覚えたい

  56. 依存関係の逆転 •制御フローはそのままに、 モジュールの依存関係を逆転させるテクニック • インタフェースを活用するワザ • これができると疎結合化が進む

  57. uGUI.Text UnityEngine.UI 例:MVPパターン •Model – View – Presenterパターン • PresenterがModelとViewの連結を行うデザインパターン

    • (Unityの場合は View = UnityEngine.UI) Presenter Model
  58. uGUI.Text UnityEngine.UI 制御フローと依存関係が一致 • PresenterがModelとViewの2つを参照する Presenter Model 依存する 依存する

  59. コード例

  60. コード例 PresenterがViewとModel を参照している

  61. uGUI.Text UnityEngine.UI 依存関係 Model Presenterパッケージ Presenter.cs 依存する 依存する

  62. 依存関係を逆転させたい •PresenterがViewを操作するのは変わらないが、 依存関係は逆にしたい Presenter Model 制御フロー 制御フロー 依存する 依存する uGUI.Text

    UnityEngine.UI
  63. やり方 1. Presentersパッケージ内にインタフェース を定義する

  64. 依存関係 Model Presenterパッケージ Presenter.cs 依存する ITextPrinter.cs uGUI.Text UnityEngine.UI

  65. やり方 2. Viewsパッケージを定義して、 そこでITextPrinterを実装する

  66. 依存関係 Model Presenterパッケージ Presenter.cs 依存する ITextPrinter.cs TextView.cs 依存する Viewsパッケージ uGUI.Text

    UnityEngine.UI
  67. やり方 3. PresenterがITextPrinterを使う

  68. 依存関係の逆転完了 Model Presenterパッケージ Presenter.cs 依存する ITextPrinter.cs TextView.cs Viewsパッケージ uGUI.Text UnityEngine.UI

    依存する
  69. 依存関係の逆転完了 Model Presenterパッケージ Presenter.cs 依存する ITextPrinter.cs TextView.cs Viewsパッケージ uGUI.Text UnityEngine.UI

    PresenterとViewの依存が逆になった! 依存する
  70. 制御フローは変わってない! Model Presenterパッケージ Presenter.cs 依存する ITextPrinter.cs TextView.cs Viewsパッケージ uGUI.Text UnityEngine.UI

    更新 R/W 依存する
  71. 依存関係の逆転 •どんなものでも依存関係は逆転できる • 制御フローはそのまま維持できる! •これを上手く活用してねっていうのが 「依存関係逆転の原則(DIP)」

  72. 設計レベルをあげるためには まとめ •制御フローを意識しよう • オブジェクト間の関係をハッキリさせる •制御フローに振り回されない対策をしよう • 設計原則をしっかり抑える • 依存関係の逆転がかなり大事

  73. (補足) • UnityのMVPパターンでここまでやるのは「冗長」 • 最初のPresenter→Viewの参照の形で困ってないなら、無理に この形に変形する必要はないです(あくまでサンプルとして挙げただけ) Model Presenterパッケージ Presenter.cs 依存する

    ITextPrinter.cs TextView.cs 依存する Viewsパッケージ uGUI.Text UnityEngine.UI
  74. クリーンアーキテクチャ(CA)

  75. すっごい雑にいうと 「君だけの最強の アーキテクチャをつくろう!」 (そのための指標に“クリーンアーキテクチャ” を使うべし!)

  76. アーキテクチャの考え •そのソフトウェアに最適なアーキテクチャは、 自分たちで模索して育てるしかない • 万能な「アーキテクチャ」は存在しない! • 自分たちのニーズに応じて自分たちでアーキテクチャを 考えて作るしかない

  77. クリーンアーキテクチャとは 様々なアーキテクチャに 共通するルールをまとめたもの こういうルールでアーキテクチャを作ると上手くいくことが多いよね、 という指標を提示するもの

  78. ざっくり言うと 過去に提唱されてきた「オニオンアーキテクチャ」「ヘキサゴナルアー キテクチャ」とかって何かどれも同じこと言ってね? ↓ つまり基本的な考えは同じでやり方が異なるだけでは? ↓ 共通部分を抜き出してみよう ↓ クリーンアーキテクチャ

  79. クリーンアーキテクチャのルール 「依存関係は上位レベルに向け一方通行にしろ」 「制御フローと依存関係を分離しろ」

  80. クリーンアーキテクチャでは •具体的に「こうしろ!」とは言ってない • レイヤ数やコンポーネント名は自由に決めていい • レイヤ/コンポーネント構成を後から変更しても良い • 小さいアーキテクチャから始めてだんだん大きくしても良い

  81. この図は何なの?

  82. 「レイヤを4つ用意するんだな…?」 「実装をこの図に上手く配置しろってことだな?」 よくある誤解

  83. 「レイヤを4つ用意するんだな…?」 「実装をこの図に上手く配置しろってことだな?」 よくある誤解

  84. この図でいいたいところ • 依存性は常に上位レベル(内側) にのみ向かってなくてはいけない! この小さい矢印が一番重要

  85. • この図はただの「たとえ」 • 「Webを例にするならこういう作り方もあるよね」くらいのノリ • レイヤ数やコンポーネントは自由に定義してよい この図に振り回されてはいけない

  86. ここの部分 •「依存関係の逆転」 を使って依存関係と制御フロー を分離しろ

  87. クリーンアーキテクチャ まとめ •クリーンアーキテクチャは「指標」 • DIYするときの指針をまとめたもの、くらいの認識 • 具体的なレイヤ定義やコンポーネント定義は一切してない •設計原則を学ぶのが一番大事 • 設計原則を抑えておかないとうまくアーキテクチャは作れない

    • CA本でも第22章までCAの話が出てこない(それだけ前提知識が必要)
  88. Unityでクリーンアーキテクチャ

  89. Q. UnityでCAってできるの? •A.「アーキテクチャの基本的な考え方」 に則ればUnityと共存できます

  90. とある考え方 •「Unity」という土台の上にクリーンアーキテ クチャで世界を作ろうとしている Unity クリーンアーキテクチャ

  91. とある考え方 •「Unity」という土台の上にクリーンアーキテ クチャで世界を作ろうとしている → この考え方だと失敗する Unity クリーンアーキテクチャ

  92. アーキテクチャの考え方 •「フレームワークと結婚するな!」 • アーキテクチャの上位レイヤにフレームワークを持ってくるな! • フレームワークの都合でアーキテクチャが振り回されるべきではない!

  93. 何と結婚するのかよく考えること •ライブラリ系もよく考えよう • UniRxとかそういうやつ • 上位レイヤで使ったほうが話が早いなら結婚してOK • ただし後から切り離しはできなくなるので覚悟が必要

  94. Unityでクリーンアーキテクチャ •Unity側がアーキテクチャ側に依存するべき • Unityとは独立してCAでコンポーネントを構成し、 それをUnity側が利用する形にするべき CAな世界 Unity 依存

  95. 実際どうするのか? 「レベル間の違う領域のハイブリッド」 をやればOK

  96. 2つの世界の両立 C# C# C# C# C# C# C# C# C#

    C# UnityEngineに べったりな世界 (レベル2) CAで構築された世界 (レベル5) 依存
  97. 使い分け •Unity非依存でロジックが構築できる部分 → クリーンアーキテクチャが適用可能 •UnityEngineを使わざるを得ない部分 → MonoBehaviourベタ書きを多用で対処する

  98. 両極端に振れず、 ハイブリッドにするという択があることを 忘れてはいけない

  99. UnityでCA まとめ •UnityでCAは利用可能 • Unityが関係無い部分はCAで作ることができる • ただしUnityEngineの機能に強く依存する部分は相性悪い •UnityとCAの関係性には注意 • UnityのアーキテクチャにCA側が振り回されてはいけない

  100. (補足)CAFU •CAFU: Clean Architecture for Unity • そっくりそのまま利用はオススメしません(断言) • 黎明期に提案されたCAのテンプレート

    • コンポーネント定義がちょっとやりすぎで煩雑 • 作者本人も「やりすぎた」と反省している • テンプレートそのまま使うのではなく、 開発規模にあったサイズのアーキテクチャを自分で考えるべき
  101. 最後のまとめ

  102. 最後に •最初から完璧なアーキテクチャは存在しない • 「アーキテクチャ」もまた開発過程で成長していく • 「プロダクトの開発・保守・運用ににかかるコストを最 低限にする」が目的であり、そこが達成できるなら 多少キタナイ部分があってもOK

  103. クリーンアーキテクチャになってなくても、 設計原則に違反する部分があっても、 Unityと密結合してる部分があっても、 それがやりやすいならそれで良い (設計しなくていいと言ってるわけではない!「バランス感を持て!」という話)

  104. 参考資料 • 世界一わかりやすいClean Architecture • https://www.nuits.jp/entry/easiest-clean-architecture-2019-09 • Adaptive Code ~

    C#実践開発手法 第2版 • https://www.amazon.co.jp/dp/B07DJ2BL4Y/ • Clean Architecture 達人に学ぶソフトウェアの構造と設計 • https://asciidwango.jp/post/176293765750/clean-architecture