Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Zenject Example ~さよならManager編~
Search
いも
August 26, 2019
Programming
2k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Zenject Example ~さよならManager編~
Roppongi.unity #04 で発表した資料です
いも
August 26, 2019
More Decks by いも
See All by いも
UnityプログラミングバイブルR6号宣伝&Unity Logging小話
adarapata
0
650
Unityテスト活動のふりかえり
adarapata
1
710
Gather.townはいいぞ その後
adarapata
1
1.7k
Unityでの開発事例
adarapata
3
23k
どこのご家庭にもあるシーンマネージャーの話
adarapata
2
8.9k
Gather.townはいいぞ
adarapata
2
2.5k
宴はいいぞ
adarapata
0
2.3k
わかった気になるモブプログラミング
adarapata
1
180
モブワークっぽいのをやっている話/Trying mobwork
adarapata
2
1.3k
Other Decks in Programming
See All in Programming
GitHubハンズオン講座 — 実務レベルのチーム開発のフローを身につけよう
junhat6
0
120
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
230
Findy - エンジニア向け会社紹介/Findy Company Deck
findyinc
6
400k
Vue Fes Japan 2026 タイムテーブル徹底解説
448jp
1
630
Are APIs Still Relevant in the AI Era?
soyuka
0
430
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
550
ハーネス設計入門 〜 基礎知識の整理から実務へのステップアップ 〜
kinopeee
20
21k
速習iPhone Duo対応
yuukiw00w
2
960
設計次第でAIコードの読む量は減らせる / designing-for-code-reading
minodriven
34
15k
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
330
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
450
Rでドレミ/Do-Re-Mi_with_R
florets1
0
120
Featured
See All Featured
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.2k
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.6k
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
590
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
300
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
270
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
3
4.3k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
20k
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
790
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.4k
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.4k
Transcript
Zenject Example ~さよならManager ~ いも(@adarapata) 2019/08/26 Roppongi.unity #04 1
今日話すこと Manager をZenject 使って滅ぼす話 Zenject を多少知ってる人向け 2019/08/26 Roppongi.unity #04 2
どこのご家庭にもあるMasterDataManager public class MasterDataManager : SingletonMonoBehaviour<MasterDataManager> { public List<PlayerData> Players
{ get; private set; } public List<EnemyData> Enemies { get; private set; } public List<ItemData> Items { get; private set; } private CsvLoader _provider; public PlayerData FindById(int id) => Players.Find(p => p.id == id); void Start() { Players = _provider.LoadAll<PlayerData>("path/to/players"); Enemies = _provider.LoadAll<EnemyData>("path/to/enemies"); Items = _provider.LoadAll<ItemData>("path/to/items"); } } 2019/08/26 Roppongi.unity #04 3
MasterDataManager の気になるポイント 責務が多い インスタンスを持つだけでなく便利メソッド生え始めた 今後あらゆるデータが追加されて肥大する香りがする テスタビリティが低い 1 メソッドテストするのに複数の要素が必要になる static なのでMasterDataManager
に依存しているコード側もテス トしにくい 2019/08/26 Roppongi.unity #04 4
MasterDataManaer の立ち位置を考える なぜMasterDataManager という名前がついてたのか? 複数の種類のデータクラスを纏めて持っているから なぜ複数の種類のデータを持ってたのか? 全部ユニークなインスタンスなのでSingleton なインスタンスに持 たせた方が楽 なぜSingleton
なのか いろんなところから簡単にアクセスできるようにしたい 各依存先に渡すのが大変だから これらを解決できるならばManager は不要になる 2019/08/26 Roppongi.unity #04 5
やっていくぞ 2019/08/26 Roppongi.unity #04 6
まずはテスト namespace Tests { public class MasterDataManagerTest { private MasterDataManager
_manager; [UnityTest] public IEnumerator FindById_Success() { _manager = new GameObject().AddComponent<MasterDataManager>(); yield return new WaitForEndOfFrame(); var playerData = _manager.FindById(1); Assert.AreEqual("Foo", playerData.name); } } } 2019/08/26 Roppongi.unity #04 7
コードのお気持ちを探る なぜSingleton なの? ← イマココ なぜMonoBehaviour なの? 2019/08/26 Roppongi.unity #04
8
なぜSingleton なの? いろんなところから簡単にアクセスできるようにしたい 各依存先に渡すのが大変だから これを解決できるならばSingleton である必要はない。 じゃあDI しましょう。 2019/08/26 Roppongi.unity
#04 9
MasterDataManager をBind する まだMonoBehaviour なのでZenject Binding でサッとやる 2019/08/26 Roppongi.unity #04
10
Project Context を作って子にする 2019/08/26 Roppongi.unity #04 11
呼ぶ側が書き直せる public class FooBehaviour { public void Foo(){ var player
= MasterDataManager.Instance.FindById(1); } } ↓ public class FooBehaviour { [Inject] private MasterDataManager manager; public void Foo(){ var player = manager.FindById(1); } } 2019/08/26 Roppongi.unity #04 12
全箇所移行完了したらMonoBehaviour に書き直す public class MasterDataManager : MonoBehaviour { public List<PlayerData>
Players { get; private set; } public List<EnemyData> Enemies { get; private set; } public List<ItemData> Items { get; private set; } private CsvLoader _provider; public PlayerData FindById(int id) => Players.Find(p => p.id == id); void Start() { Players = _provider.LoadAll<PlayerData>("path/to/players"); Enemies = _provider.LoadAll<EnemyData>("path/to/enemies"); Items = _provider.LoadAll<ItemData>("path/to/items"); } } 2019/08/26 Roppongi.unity #04 13
コードのお気持ちを探る なぜSingleton なの? :done: なぜMonoBehaviour なの? ← イマココ 2019/08/26 Roppongi.unity
#04 14
なぜMonoBehaviour なの? 初期化処理( ロード部分) を呼びたい 誰も初期化を呼ぶ人がいないのでStart() を使いたい これを解決できるならばMonoBehaviour である必要はない。 2019/08/26
Roppongi.unity #04 15
IInitializable を実装してみよう public class MasterDataManager : MonoBehaviour, IInitializable { public
List<PlayerData> Players { get; private set; } public List<EnemyData> Enemies { get; private set; } public List<ItemData> Items { get; private set; } private CsvLoader _provider; public PlayerData FindById(int id) => Players.Find(p => p.id == id); public void Initialize(){ Players = _provider.LoadAll<PlayerData>("path/to/players"); Enemies = _provider.LoadAll<EnemyData>("path/to/enemies"); Items = _provider.LoadAll<ItemData>("path/to/items"); } } 2019/08/26 Roppongi.unity #04 16
IInitializable Zenject が提供するinterface やることは初期化のみ 実装すると、依存関係解決後にInitialize() をコールしてくれる 裏側でInitializableManager が動いてる。 2019/08/26 Roppongi.unity
#04 17
不要になったのでMonoBehaviour をやめよう public class MasterDataManager : IInitializable { public List<PlayerData>
Players { get; private set; } public List<EnemyData> Enemies { get; private set; } public List<ItemData> Items { get; private set; } private CsvLoader _provider; public PlayerData FindById(int id) => Players.Find(p => p.id == id); public void Initialize(){ Players = _provider.LoadAll<PlayerData>("path/to/players"); Enemies = _provider.LoadAll<EnemyData>("path/to/enemies"); Items = _provider.LoadAll<ItemData>("path/to/items"); } } 2019/08/26 Roppongi.unity #04 18
MonoBehaviour じゃなくなったのでInstaller を書こう public class MasterDataInstaller : MonoInstaller { public
override void InstallBindings() { Container.Bind<MasterDataManager>().AsSingle(); } } 2019/08/26 Roppongi.unity #04 19
テストも少し書き直そう namespace Tests { public class MasterDataManagerTest { private MasterDataManager
_manager; // MonoBehaviour じゃないのでEditTest で十分 [Test] public void FindById_Success() { _manager = new MasterDataManager(); _manager.Initialize(); var playerData = _manager.FindById(1); Assert.AreEqual("Foo", playerData.name); } } } 2019/08/26 Roppongi.unity #04 20
今一度MasterDataManaer の立ち位置を考える なぜMasterDataManager という名前がついてたのか? 複数の種類のデータクラスを纏めて持っているから なぜ複数の種類のデータを持ってたのか? 全部ユニークなインスタンスなのでSingleton なインスタンスに持 たせた方が楽 なぜSingleton
なのか いろんなところから簡単にアクセスできるようにしたい 各依存先に渡すのが大変だから 2019/08/26 Roppongi.unity #04 21
今一度MasterDataManaer の立ち位置を考える なぜSingleton なのか => DI でOK なぜ複数の種類のデータを持ってたのか? => 直接Inject
できるなら 纏める必要はない なぜMasterDataManager という名前がついてたのか? => 纏める必 要ないなら不要では? 2019/08/26 Roppongi.unity #04 22
データ持ってるクラスを独立 public class PlayerDataRepository { private readonly List<PlayerData> _resources; public
PlayerDataRepository(IResourceLoadProvider _provider) { _resources = _provider.LoadAll<PlayerData>("path/to/players"); } public PlayerData FindById(int id) => _resources.Find(p => p.id == id); } public class MasterDataInstaller : MonoInstaller { public override void InstallBindings() { Container.BindInterfacesAndSelfTo<MasterDataManager>().AsSingle(); Container.BindInterfacesAndSelfTo<PlayerDataRepository>().AsSingle(); Container.Bind<IResourceLoadProvider>().To<CsvLoader>().AsSingle(); } } 2019/08/26 Roppongi.unity #04 23
PlayerDataRepository だけ差し替えて動作は変えない public class MasterDataManager : IInitializable { public List<EnemyData>
Enemies { get; private set; } public List<ItemData> Items { get; private set; } [Inject] private IResourceLoadProvider _provider; [Inject] private PlayerDataRepository _playerDataRepository; public PlayerData FindById(int id) => _playerDataRepository.FindById(id); public void Initialize() { Enemies = _provider.LoadAll<EnemyData>("path/to/enemies"); Items = _provider.LoadAll<ItemData>("path/to/items"); } } 2019/08/26 Roppongi.unity #04 24
新規に書くときは直接Repository を渡してあげればよい public class FooBehaviour { [Inject] private PlayerDataRepository _repository;
public void Foo(){ var player = repository.FindById(1); } } 2019/08/26 Roppongi.unity #04 25
Test もZenject 対応 public class MasterDataManagerTest : ZenjectUnitTestFixture { public
class MockLoader : IResourceLoadProvider { public List<T> LoadAll<T>(string path) => new List<T>(/* なんかいい感じ生成 */); } private MasterDataManager _manager; [Test] public void FindById_Success() { Container.Bind<MasterDataManager>().AsSingle(); Container.BindInterfacesTo<MockLoader>().AsSingle(); Container.Bind<PlayerDataRepository>().AsSingle(); _manager = Container.Resolve<MasterDataManager>(); _manager.Initialize(); var playerData = _manager.FindById(1); Assert.AreEqual("Foo", playerData.name); } } 2019/08/26 Roppongi.unity #04 26
さよならManager 2019/08/26 Roppongi.unity #04 27
まとめ Zenject 使えばインスタンスを集約するだけのクラスは不要になる Manager と呼ばれてるものの役割を見直してみよう もしかしたらインスタンスを集めてるだけのクラスかもしれない 漸進的に変更できる方法を選ぼう 旧コードと新コードが一時は同居できるようにする テストを書くと安心感ある テストを書くと安心感ある
テストを書くと安心感ある 2019/08/26 Roppongi.unity #04 28
宣伝 Zenject 本書いてます 技術書典7 こ20D 前後編になりました 〆切と印刷費に負けた ごめん 今回は前編を出します DI
の話~Scene 跨ぎのBind くらいまで 2019/08/26 Roppongi.unity #04 29