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

Unityにおける設計パターン

torisoup
February 19, 2021

 Unityにおける設計パターン

torisoup

February 19, 2021
Tweet

More Decks by torisoup

Other Decks in Technology

Transcript

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

    View Slide

  2. 自己紹介
    •とりすーぷ
    • @toRisouP
    • VR系の開発してる
    • Microsoft MVP 2018~
    • Developer Technologies
    • 最近はVRChatしてます
    illustrations by kota(@kt_kkz)さん

    View Slide

  3. 本を出しました
    •「 UniRx/UniTask完全理解
    より高度なUnity C#プログラミング」

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide


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

    View Slide

  16. ダメなコードの例
    •スパゲッティコード
    • オブジェクトとオブジェクトがどう関係するのかがわからない状態
    • 影響範囲が謎でどこを触っていいかわからない状態
    • ちょっとした機能変更にもすごく時間がかかる状態
    •外部モジュールと一蓮托生した状態
    • 「生殺与奪の権を他人に握らせるな」状態
    • 外部モジュール(ライブラリやフレームワーク)の機能に強く依存しすぎた結果、
    外部モジュールの仕様変更に振り回されてしまうような状態

    View Slide

  17. コードは腐る
    •少しの油断でコードはすぐにダメになる
    • 割れ窓理論
    • どんな状況においても「設計を守る」を強く意識して
    開発に臨む必要がある

    View Slide

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

    View Slide


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

    View Slide

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

    View Slide


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

    View Slide

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

    View Slide

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

    View Slide


  24. •あります。
    • FWに振り回されるのは別にUnityに限った話ではない
    • Unityだけが特別なわけではない
    • 「(様々な)FWとの上手な付き合い方」はソフトウェア開発の
    一般的な問題
    • その対策として「クリーンアーキテクチャ」などの
    ○○アーキテクチャが考案されてきた

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  32. レベル 3:モジュール化が意識される
    • 機能ごとにモジュール化される
    • モジュールの安定性が考慮される
    • 機能の再利用性が考慮される
    • asmdefが切られたりする
    (コンポーネント、という呼び方もあるが
    UnityのComponentと被るのでここでは
    モジュールと呼びます)
    C# C#
    C#

    View Slide

  33. レベル 4:ピュアなC#を活用する
    • ピュアなC#がメインロジック化
    • MonoBehaviourを使わない実装が増える
    C# C#
    C# C#
    C#
    ←Unity依存はここだけ

    View Slide

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

    View Slide

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

    View Slide

  36. レベル分け
    5:アーキテクチャが意識される
    4:ピュアなC#が活用される
    3:モジュール化が意識される
    2:制御フローと依存関係が分離する
    1:フローが整理される
    0 : 設計なし ハイリスク
    ローリターン
    どんな状況でも最低限達成したい
    できてればよりGood
    コストは高いが
    メリットも大きい

    View Slide

  37. 設計レベルをあげるメリット
    •長期開発における機能追加・保守・運用の
    コストが下がる
    • 機能追加や仕様変更に柔軟に対応できるようになる
    • 大人数で開発しても破綻しにくい体制が作れる

    View Slide

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

    View Slide

  39. トレードオフ

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

    View Slide

  40. 大事なこと
    •チームがやりやすいレベルで止めてもよい
    • 難しいと感じたらレベルを下げるのもあり
    • ただし最低でも「レベル1」は維持したい
    •設計レベルをハイブリッドにしても良い
    • チューニングしたいところはUnityと密にする
    • Unityと関係ない部分はできるだけピュアC#で書く

    View Slide

  41. ハイブリッドなやり方
    C# C#
    C#
    C# C# C# C#
    C#
    C#
    C#
    レベル2で
    構成された領域
    レベル5で
    構成された領域
    依存

    View Slide

  42. 設計レベル まとめ
    •Unityにおける設計をレベルわけしてみた
    • 勝手に決めたレベルなので異論反論は受け付ける
    •どのレベルで開発するかバランスをみるの大事
    • 理想としてはレベル5を目指していきたいが、
    現実はそこまでいけることは稀
    • どこかでバランスを取る必要がある

    View Slide

  43. 設計レベルをあげるためには

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  52. 設計原則
    •SOLID原則
    • SRP、OCP、LSP、ISP、DIP
    •コンポーネントの凝縮性の原則
    • REP、CCP、CRP
    •コンポーネントの結合性の原則
    • ADP、SDP、SAP
    ←より大局的な視点での設計の話
    (レベル3以上に必須)
    ←より大局的な視点での設計の話
    (レベル3以上に必須)

    View Slide

  53. 詳しくは
    •この本読んでください

    View Slide

  54. 必要なこと
    5:アーキテクチャが意識される
    4:ピュアなC#が活用される
    3:モジュール化が意識される
    2:制御フローと依存関係が分離する
    1:フローが整理される
    0 : 設計なし 制御フローを意識しよう
    SOLID原則を学ぼう
    コンポーネントの
    凝縮性・結合性の原則
    を学ぼう

    View Slide

  55. 一番大事なテクニック
    •「依存関係の逆転」
    • 依存関係逆転の原則(DIP)を行うのに必須
    • これができるだけでもかなり違うので絶対覚えたい

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  59. コード例

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  69. 依存関係の逆転完了
    Model
    Presenterパッケージ
    Presenter.cs
    依存する
    ITextPrinter.cs TextView.cs
    Viewsパッケージ
    uGUI.Text
    UnityEngine.UI
    PresenterとViewの依存が逆になった!
    依存する

    View Slide

  70. 制御フローは変わってない!
    Model
    Presenterパッケージ
    Presenter.cs
    依存する
    ITextPrinter.cs TextView.cs
    Viewsパッケージ
    uGUI.Text
    UnityEngine.UI
    更新
    R/W
    依存する

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  74. クリーンアーキテクチャ(CA)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  78. ざっくり言うと
    過去に提唱されてきた「オニオンアーキテクチャ」「ヘキサゴナルアー
    キテクチャ」とかって何かどれも同じこと言ってね?

    つまり基本的な考えは同じでやり方が異なるだけでは?

    共通部分を抜き出してみよう

    クリーンアーキテクチャ

    View Slide

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

    View Slide

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

    View Slide

  81. この図は何なの?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  87. クリーンアーキテクチャ まとめ
    •クリーンアーキテクチャは「指標」
    • DIYするときの指針をまとめたもの、くらいの認識
    • 具体的なレイヤ定義やコンポーネント定義は一切してない
    •設計原則を学ぶのが一番大事
    • 設計原則を抑えておかないとうまくアーキテクチャは作れない
    • CA本でも第22章までCAの話が出てこない(それだけ前提知識が必要)

    View Slide

  88. Unityでクリーンアーキテクチャ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  96. 2つの世界の両立
    C# C#
    C#
    C# C# C# C#
    C#
    C#
    C#
    UnityEngineに
    べったりな世界
    (レベル2)
    CAで構築された世界
    (レベル5)
    依存

    View Slide

  97. 使い分け
    •Unity非依存でロジックが構築できる部分
    → クリーンアーキテクチャが適用可能
    •UnityEngineを使わざるを得ない部分
    → MonoBehaviourベタ書きを多用で対処する

    View Slide

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

    View Slide

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

    View Slide

  100. (補足)CAFU
    •CAFU: Clean Architecture for Unity
    • そっくりそのまま利用はオススメしません(断言)
    • 黎明期に提案されたCAのテンプレート
    • コンポーネント定義がちょっとやりすぎで煩雑
    • 作者本人も「やりすぎた」と反省している
    • テンプレートそのまま使うのではなく、
    開発規模にあったサイズのアーキテクチャを自分で考えるべき

    View Slide

  101. 最後のまとめ

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide