简体   繁体   中英

When using constructor dependency injection, is it normal for the injector class to become a large list of constructors?

I was not sure if better to ask here, or on GameDev Stack Exchange. I believe it carries over to general programming.

Context

  • I am using Unity3D and building an Online Multiplayer Game.
  • I would like to use Constructor Dependency Injection, so no "magic" reflection to keep things simple and be able to view my dependencies more clearly.
  • I would like to have Sub Injector Classes that will resolve dependencies.

For example when I spawn a Player into the game world the root PlayerScript will be an injector that will resolve all of the players dependencies.

The Player will have a service collection and then it will construct each service the player needs to function.

Problem

The Player injector becomes a large list of constructing the services the player needs. I am trying to use SOLID principles, so by splitting my player services into many smaller services. This might mean having 20-30 services on the player. It just feels wrong to have 20-30 lines of code constructing each services and passing them their dependencies.

This is kind of what it is looking like if it wasn't in Unity3D.

Outside of Unity Example


        //PlayerMovement
       Services.Add<CharacterController>(new CharacterController(Inj 1,Inj 2, Inj 3));

        //PlayerInputs
        Services.Add<UIInputs>(new UIInputs(Inject 1,Inj 2, Inj 3));
        Services.Add<InventoryInputs>(new InventoryInputs(Inject 1,Inj 2));
        Services.Add<MovementInputs>(new MovementInputs(Inj 1,Inj 2, Inj 3));
        Services.Add<InteractionInputs>(new CrossHair(Inj 1,Inj 2));

        //PlayerInventory
        Services.Add<InventoryStateManager>(new InventoryStateManager(Inj 1,Inj 2, Inj 3));
        Services.Add<PlayerInventory>(new PlayerInventory(Inj 1,Inj 2, Inj 3));
        Services.Add<CursorInventory>(new CursorInventory(Inj 1,Inj 2, Inj 3));
        Services.Add<ActionBarInventory>(new ActionBarInventory(Inj 1,Inj 2, Inj 3));

        //PlayerUI
        Services.Add<PlayerUI>(new PlayerUI(Inj 1,Inj 2, Inj 3);
        Services.Add<InventoryViewManager>(new InventoryViewManager(Inj 1,Inj 2, Inj 3));
        Services.Add<PlayerInventoryView>(new PlayerInventoryView(Inj 1,Inj 2, Inj 3));
        Services.Add<CursorInventoryView>(new CursorInventoryView(Inj 1,Inj 2));
        Services.Add<ActionBarInventoryView>(new ActionBarInventoryView(Inj 1,Inj 2, Inj 3));
        Services.Add<StorageInventoryView>(new StorageInventoryView(Inj 1,Inj 2));
        Services.Add<ActionBarSelection>(new ActionBarSelection(Inj 1,Inj 2, Inj 3));
        Services.Add<CrossHair>(new CrossHair(Inj 1,Inj 2, Inj 3));

Unity Differences

Only read if interested in how I implemented using Unity.

In unity you cannot construct monobehaviour classes. So instead you have to find all of your dependencies that already exist on the player.

I did that by adding IService interface to all Monobehaviours in the Scene. When Player Spawns into the server it will find all IService s, and then I will inject the dependencies by calling an initialization function on each service.

Question

Is it normal to have a lot of services constructed in one injector class?

Please correct me if I have a misunderstanding here.

It is totally normal that injections constructed in one class, that's actually what it is for. You can easilly control and see your dependencies at single place but you should be aware that if you're services are monobehaviour then probably it is not a service. That class Also called as Context generally and there can be more than one context.

You're current injections aren't services, they are more like views that player see. generally views don't do logic but it is also a must that monobehaviour be inside views like player movement.

You can think that views are unity related things and you clamp unity related things as views. Services are used widely as the game runs and don't have related things to the unity. ie IAuthService for Steam, Epic games, Xbox etc or SettingsService for different OSs.

Many people names their classes names as PlayerController, UiManager/Service without realazing. What they try to achieve looks like always MVC but in a messy way. What i said is also looks almost identical to MVC(S). I think you should look at MVC(S) architectural pattern and get some inspiration.

There's already a framework that supports MVCS + IoC, it is Strange , you should def.netly check it.

That is not unusual. The more you make use of dependency injection, the more you end up pushing the responsibility of object creation upwards, until at some point you reach a "ceiling". This is where you should have your Composition Root ; the place where you compose the object graph.

In DI circles it's actually a common recommendation, that one should preferably only have a single Composition Root in the entire application.

However, I personally think that in the world of Unity, where scenes and prefabs also offer a powerful way to compose objects together, it can be better to give prefabs and scene objects - or even individual components, actually - their own little Composition Roots to hold references to local services that should be injected to clients. I see you also ended up doing something similar with your IService-based implementation!

I rolled out my own DI solution for Unity ( Init(args) ) a couple of months back. In it I went with an approach where a separate "Initializer" can be generated for each component, and it takes on the responsibility of injecting all the dependencies to its client component.

While some hardcore DI purists would probably find this sort of multi-composition-root approach objectionable, it does come with a lot of benefits as well.

For example, when working in a team with multiple developers, having only a single Composition Root can result in more merge conflicts, when multiple feature teams end up modifying the object graph in their own branches.

So my recommendation is not to worry too much about what is "normal", but focus on actual pain points in your development process and adjust the solutions you are using so as to get rid of these as much as you can:)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM