Search Unity

Dependency Injection + Unity - Introducing Suice

Discussion in 'Scripting' started by disturbing, Oct 25, 2013.

?

What's your experience with Dependency Injection?

  1. I've never heard of it!

    24.1%
  2. I don't believe in Unit Testing or Dependency Injection

    3.4%
  3. I perform Unit Testing without Dependency Injection

    8.6%
  4. I created or use another Dependency Injection slution

    63.8%
  1. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    Hey Unity Devs!

    If you never have heard of Dependency Injection before, check out this java article here.

    There's a sweet implementation and topic on Dependency Injection using Unity's IoC container here. This solution involves more manual labor than needed with Guice's solution to DI.

    Suice has been battle tested on over 5 MILLION devices to date across iOS/Android/PC on my personal projects. It requires Unity 4.3 due to mono requirements.

    Links:

    Suice C# Open Source project(Requires .NET 3.5 or Unity 4.3+) https://github.com/Disturbing/suice
    Unity Suice Example Project (Requires 4.6+): https://github.com/Disturbing/UnitySuice

    Overall - the examples give enough information of what's going on here. Although - there's tons of more code needed to make Unity fully testable - especially at the monobehaviour/gameobject level.

    Once demand rises - I'll work on creating a test suite for Unity + Suice, making Unity fully testable. Once demand rises even further, I can look into releasing my epic toolkits that speed up development and testing for my games for cheap.
     
    Last edited: Dec 27, 2014
  2. Trilusion

    Trilusion

    Joined:
    Mar 20, 2013
    Posts:
    60
    I was looking into StrangeIOC framework yesterday, looks interesting, though IOC principle and DI is something new for me.
     
  3. ShuuGames

    ShuuGames

    Joined:
    Jun 25, 2013
    Posts:
    188
    As for me, I've used singleton pattern mixed with component drag-drop in Editor. The MonoBehaviour with singleton method has always been abstract and implementations are in subclass. This way I can create prefabs, that contain one or many singleton classes. Drag-dropping these prefabs into the scene hierarchy has been my way to configure dependencies. Also usually everything I have in my scene are prefabs (imho, more version control friendly). Setting up a level or something is then just a matter of dragging right prefabs into hierarchy, that's all.

    As for unit testing... not really used those in traditional sense, I think. Usually it's been enough to separate some concept, like UI and then hook it to dummy level to simulate through all possible scenarios without having to actually play the. Same with player, enemies, interfaces with other systems.

    But I'm looking forward to your solution, I've used IoC elsewhere, but since Unity hates interfaces so much, I've never really tried to bring in full IoC framework. Not a good idea, I've thought so far. But if there is a solid and not bloated IoC solution for Unity, I'm probably going to give it a try. I see the potential in here.
     
  4. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    Hi! I'm the guy behind StrangeIoC. Cool to see other people looking at DI in Unity!

    We use NUnit for unit testing, but the main problem we have is the test-resistance of MonoBehaviours. All our best solutions involve abstracting MB functionality, which just kinda works. Ninject has a novel solution to this issue...by basically mocking the entire Unity engine. The problem (IMO) is that it makes your code too reliant on Ninject itself. Very interested to find out how you plan to address this issue!

    Best of luck!
     
  5. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    Good to hear people know what's going on! IoC/DI is something I hope more unity devs will use.

    @ShuuGames - Mind giving me your take on how exactly Unity hates interfaces? I have my set of problems with Unity + Interfaces, but want to make sure we are on the same page.

    Dragging and dropping Dependencies via inspector sounds pretty hectic! My take on how DI works with Unity involves separating MonoBehaviours/GameObjects as strictly 'entities', and Dependencies (Services, controllers, etc) manage them.

    Welcome to the thread @srimarc! I love the details you have present with solution and have read thoroughly through your repository! Definitely a good take on IoC, and one of the best avail IMO. The MVCS methodology + DI does wonders for how to solve the simplest and most complex problems.

    The most distasteful problem that turns people away from DI which Guice solves is automating (JIT) bindings, eager singletons, constructor injection (pretty standard in most DI frameworks outside of C#/Unity), and modules.

    We also have added custom requirement handlers for Dependencies (if needed), making dynamic binding easy with use cases like platform (android/ios,etc) requirements.

    We are going to make it as light weight, yet extensible as possible.

    Few questions for you all:

    What are your worst problems aside from testing with IoC / DI + Unity that you've experienced so far?
    Do you use built in editor testing plugins, or setup separate projects for Testing?

    I'll share my two cents on the next post about MonoBehaviours + DI as well in my next reply - Mocking the Unity Engine is definitely a problem to be solved, and I hope our solution will aid that!
     
    Last edited: Oct 27, 2013
  6. AnomalusUndrdog

    AnomalusUndrdog

    Joined:
    Jul 3, 2009
    Posts:
    1,553
    Hey all,

    This topic is of interest to me. I know how a jumbled mess it can get when trying to initialize sub-systems in my code in a correct order. I had been using mostly what could be considered singletons for most of my code. In some respects, you could say they are just as bad as global variables.

    I do get the idea behind dependency injection, but I just couldn't get around understanding how StrangeIoC works. I don't know, maybe I just need a tutorial.

    I use unit testing, but only for low-level parts of my code (the deterministic parts; non-MonoBehaviour classes; non-physics-related). I use SharpUnit and it works sufficiently for what I do.

    Not sure what's the connection between unit testing and DI though.
     
  7. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    As for StrangeIoC, I'm sure you can contact the source owner who will help you out and improve his examples.

    I was in a tweeter conversation about how this will be the 4th DI solution available. So there's other alternatives as well. But if you wait a few more weeks, I'll definitely have something up soon.

    --

    The DI pattern helps you make your code more testable and modular. Giving classes and functions a single responsibility while being able to abstract logic to work entirely on it's own without being constricted to a specific implementation of a 'dependency' makes some damn beautiful code.

    Here's some really good rules in making clean and modular code. DI provides a solution for all of these error prone warnings.

    As for testing, dependency injection forces you to code in a testable manner to easily mock classes. For instance, ClassA uses ClassB usually with ClassB.Instance().SomeFunction using the Singleton Pattern. Instead, ClassB is injected through the constructor of ClassA where you store it locally and use it directly. DI not only allows you to mock ClassB, but now you are strictly testing the logic of ClassA.

    Hope that helps!
     
  8. srimarc

    srimarc

    Joined:
    Nov 1, 2012
    Posts:
    86
    I'm sorry to hear that, Anomalus. I've been getting feedback recently that we need more/better tutorials. We'll be working on that. In the meantime, you might look at http://unity.bfowle.com/category/unity/ where bfowle's written a couple of nice ones that include unit testing.

    BTW, I don't want to hijack disturbing's thread, so if anyone wants to talk Strange, feel free to post over on our thread (http://forum.unity3d.com/threads/18...rol-Framework-for-Unity-amp-C/page6?p=1369829).

    disturbing, I'll be looking forward to seeing what you come up with!
     
  9. ShuuGames

    ShuuGames

    Joined:
    Jun 25, 2013
    Posts:
    188
    There are two basic ways to abstract functionality: abstract classes and interfaces. Abstract classes are nice, Unity engine "supports" them. FindObjectByType and GetComponent return derived classes if there are any. MonoBehaviour fields can have abstract class types and they can be assigned in Inspector. Internally GetComponent is well optimized (I certainly hope so! Unity devs can't be so irresponsible to not make it fast). Look at the scripting reference, see any interfaces, no, because there are none. All is done with inheritance hierarchies. If one wants to use interfaces for most of the abstractions, then the best case is it's going to be a mix of abstract class and interface patterns. Interfaces are just not treated as first class citizens in Unity engine. A nice DI framework will change that for good and bigger projects will benefit from it for sure.
     
  10. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    Well said and agreed!

    I hope to have something to show by the end of this week.

    Testing all kinds of goodies :).
     
  11. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    I'm having issues with creating a dynamic proxy for a class in runtime via Unity. Anyone have a solution for this?

    ---

    Update on what's done so far (sorry for the lack of posting!) Day job took most of my time in the past couple of months and I haven't had any time to touch any code :( I know depressing!

    JIT Dependencies is fully supported -

    [Singleton] [ImplementedBy] [ProvidedBy] implementation

    Circular Dependency is supported via Field Injection, but not constructor injection atm until I figure out how to make a dynamic proxy.

    Modules are fully supported with manual binding, static binding instances, and provide functions.

    Looking good... Very good :)
     
    Last edited: Jan 12, 2014
  12. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    Bit of an update!

    Everything is done and working other than circular dependency support.

    I will be able to set that up with Interfaces only!!! Which will force everyone to create interface abstractions to their services/dependencies to support that feature.

    Other then that. I just need to setup documentation and make sure it compiles on Unity 4.x iOS. It works great on Web/Android/PC/OSX at the moment.

    I'm also not working for goon studios anymore so, I will be starting a new thread once this project is launched.
     
  13. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    What's the dynamic proxy for? You should take a look at how libraries like AutoFac do it. AutoFac builds out some sort of object graph and instantiates to fulfill the graph. It handles binding at the constructor level or binding at the field level with circular dependencies. I'm not sure how it does it under the covers but you could take a look since it's open source:

    http://code.google.com/p/autofac/
     
  14. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    They There,

    Thanks for the suggestion. I'll check out how they do it for sure!

    I don't like the idea of wrapping it with Lazy<T> unfortunately and would like a more automated way.

    I'm currently looking to see if C#"s RealProxy will work on all Unity3D Platforms.

    The dynamic proxy will be used for circular dependency.
     
  15. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    RealProxy is an abstract... you'll have to create a derived class from it. Also, it's in the System.Runtime.Remoting namespace so double check to make sure that is supported across platforms in Unity.
     
  16. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    For Sure! Thanks for the heads up.

    I'm more or less just saying that RealProxy may be a solution. If not, I'll for sure look into Castle.Core's Dynamic Proxy which is used in the AutoFac solution you pointed out.

    The functions needed look like it's gonna work from the MonoCompatibility page. Just need to now find some time :).
     
  17. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    Tons of work to be done on the documentation and code itself, but it's working great.

    I have tested it on Android, iOS, PC and Webplayer and it's working great on Unity 4.3.3+

    Unfortunately ciruclar dependency is definitely not possible in C#.

    I've attached an example project, although, here's the github for the Suice util.

    Looking forward to your feedback:

    https://github.com/Disturbing/suice

    More or less, the Just In Time Support which only works in Unity 4.x is the kicker. Here's some code examples:

    Singleton example:

    Code (csharp):
    1.  
    2. using CmnTools.Suice;
    3.  
    4. [Singleton]
    5. public class StaticData : InitializeDependency
    6. {
    7.     private readonly XmlParser _xmlParser;
    8.  
    9.     private string data;
    10.  
    11.     [Inject]
    12.     public StaticData (XmlParser xmlParser) {
    13.         _xmlParser = xmlParser;
    14.     }
    15.  
    16.     public void Initialize() {
    17.         this.data = _xmlParser.ReadData(“Path/To/File”);
    18.     }
    19. }
    20.  
    21.  
    Implemented By Example:

    Code (csharp):
    1.  
    2. [ImplementedBy(typeof(StaticData))]
    3. public interface IStaticData
    4. {
    5.     void LoadData();
    6.     void UnloadData();
    7.  
    8.     Template GetTemplate(int templateId);
    9. }
    10.  
    Provided By Example:

    Code (csharp):
    1.  
    2. using CmnTools.Suice;
    3.  
    4. [ProvidedBy(typeof(ItemExampleProvider))]
    5. public interface IItemExample {
    6.     int GetItemId();
    7. }
    8.  
    9. public class ItemExampleProvider : Provider<IItemExample> {
    10.  
    11.     private readonly IStaticData _staticData;
    12.  
    13.     public override IItemExample Provide() {
    14.         IItemExample itemExample = new ItemExample(_staticdata.GetTemplate(Rnd.Get(0,1))(;
    15.  
    16.         return itemExample;
    17.     }
    18. }
    19.  
    20.  
    If you want to create singletons / implementations based on platform or runtime conditions, you can create a module and register it to the injector.

    Code (csharp):
    1.  
    2. // Code in injector monobehaviour class
    3. private static void SetupInjector()
    4. {
    5.     _injector = new Injector();
    6.     RegisterModules(_injector);
    7.     _injector.Init();
    8. }
    9.  
    10. private static void RegisterModules(Injector injector)
    11. {
    12.     injector.RegisterModule(new ExampleModule());
    13. }
    14.  
    15. // Example module
    16.  
    17. using CmnTools.Suice;
    18. public class ExampleModule : AbstractModule
    19. {
    20.  
    21.     public override void Configure() {
    22.           Bind<INetworkService>().To<NetworkService>().In(Scope.SINGLETON);
    23.     }
    24.  
    25.         [Provides(Scope.NO_SCOPE)]
    26.         public IConnection provideNewNetworkConnection(ISocket socket) {
    27. #if UNITY_IOS
    28.            IConnection connection = new IosConnection(socket);
    29. #else
    30.               IConnection connection = new DefaultConnection(socket);
    31. #endif
    32.            connection.Connect();
    33.            return connection;
    34.        }
    35. }
    36.  
    As for how to test Monobehaviours. This can be solved by the following rules:

    1) Create an interface for the Monobehaviour that you want to test.
    2) Ensure all business logic in the Monobehaviour is related to 3D Scene logic only
    3) Create some sort of service/controller to manage instantiate and manage the Monobehaviour. Write tests for that service/controller and Mock the interface you created on the monobehaviour to make sure the appropriate calls are being made.

    If you want to test the monobehaviour class itself, you can do this with UnityTestTools + Integration Testing.

    Definitely looking forward to your thoughts.

    ~DisTurBinG
     
  18. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    Here's the attached example project:
     

    Attached Files:

  19. Patico

    Patico

    Joined:
    May 21, 2013
    Posts:
    886
    Thanks, you've made a big work.
     
  20. cstaylor8311

    cstaylor8311

    Joined:
    Mar 11, 2013
    Posts:
    9
    This is a fun an interesting concept. DI can work wonders for clarity at the framework levels. I come from a background of multiple inheritance and DI allows the programmer to stay sane in an environment like that.

    I look forward to checking this implementation out in c#
     
  21. Patico

    Patico

    Joined:
    May 21, 2013
    Posts:
    886
    That's the truth! :)
     
  22. cannon

    cannon

    Joined:
    Jun 5, 2009
    Posts:
    751
    Wow, been looking for a DI solution all of last year, and I missed Zenject (that, or we we really needed something Flash compatible at the time).

    I've looked at StrangeIOC, but since we were looking for something a bit closer to Ninject we've been making do with rolling our own barebones DI.

    This looks like it will fill the spot. Thanks for making this available!
     
  23. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    Glad you guys are digging it. I really need to find the time to add the documentation!

    There's a lot of great use cases with this system in being able to control UI, broadcast events, listen to packet events, all kinds of amazing stuff. Please ask me if you want any examples with your use cases, and I will create it for you.

    Here's a simple screenshot example of how to write a unit test against a Mono Behavior:

    http://puu.sh/6TR5B.png

    The Resource Factory is a wrapper for Resources.Load.

    I have the MonoBehaviour GuiView which implements IGuiView.

    So what I was able to do, is mock the GuiView by using NSubtsitute, allowing me to test the functionality of Monobehaviour itself. This is shown in the _guiView.Received().SetLabel....

    I look forward to making examples and improving the documentation during my fly time to Sweden this month :). 36 hours in air, should be enough time!
     
  24. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    Hey guys -

    My DI Framework has been treating me well so far and I think it's fairly stable. I have a couple optimizations I can add to it and was thinking that the out of the box C# DI framework with somewhat broken documentation could use a face lift.

    If there's 10 people who's interested in me writing examples of how to write badass testable code with a sweet most efficient (not bias, it's proven), DI framework to help support Unity trying to push testable code with Monobehaviours, etc. - I'm willing to spend some time.

    Just want to see at least 10 people interested in such effort before I spend my time making my work public and documenting it.

    Let me know!

    Again, my DI Framework provides:

    * JIT Dependency Injection (No need to map anything, no xml, no binding, nothing
    * Mapped Dependency Injection using Modules (Custom mapping where you can bind depending on platforms, etc)
    * Out of the box Factory pattern dependency injection
    * Efficiency - Non monobehaviour based DI. Doing this is extremely inefficient and happens too often when loading multiple levels, etc. DI only happens on startup using reflection and adds only .25s on an iPhone4 to your startup time with project sizes of with over 500 DI based files.
    * Full Control of Memory - Being able to wipe out your entire game state or accessing all classes in memory for things like custom event listening or networking can be done at startup.
     
  25. favianndt

    favianndt

    Joined:
    Nov 19, 2013
    Posts:
    2
    I know I'm a but late to the party but I sure would be interested in that framework. Since I'd love to clean up our code base and add some unit test. Not sure how it will work with monobehaviors.

    Anyways a couple of examples would be really amazing. :)
     
  26. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    I'm blocking some time this christmas to work on something. Maybe even submit it to the asset store.

    As for monobehaviours, they only act as APIs to the 3D/2D world. IE: When you want to control, move, interact with the 'view' layer. Suice and DI can create services, controllers, managers to spawn and manage the 'view'. All logic goes into the controller which is testable and anything regarding monobehaviours can be done with integration tests as Unity Test Tools shows.

    I'm thinking of just making a 2D game example to show it off a bit easier over xmas break.
     
  27. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    Well - xmas break came up.

    I've made some extensive examples in a Unity project and have also optimized Suice a bit.

    Documentation is a bit outdated, but will look into getting it updated soon. The code is fairly clean with enough comments to get you guys started!

    Please take a look and let me know your thoughts:

    https://github.com/Disturbing/UnitySuice
     
  28. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Your framework is an interesting implementation of DI! I didn't know Guice (I have to admit that my Java experience is only with games and without any DI component) and it looks a very lightweight container, just like yours.

    Looking at the source of Suice, it's clean and simple to follow, I'd even say... direct to the point! The task manager concept at the Unity examples is also very nice (even with you pointing out that it may benefit from some refactoring).

    I'm glad to see that IoC/DI is becoming common ground for Unity devs, with some great containers available. Although your topic is from 2013, my research on the theme during 2014 didn't lead me here (until now).

    In fact, after evaluating some DI solutions for Unity in early 2014, I ended up creating a simple container (currently not as simple as the original ideas of that time) based on the great article from Sebastiano that you reference in the first post.

    Why not release Suice in the Asset Store? I think this could spread the word about your work and give devs another great DI option.
     
  29. disturbing

    disturbing

    Joined:
    Dec 12, 2010
    Posts:
    127
    Thanks for the feedback dude. I need to spend some solid time adding documentation and I also have to add one more important feature to top it off. I also want to explore IL2CPP to see if it supports circular dependency / dynamically generated proxies.

    I'm humbled to hear your feedback. I hope we can push the benefits of DI to make it a standard with more Unity Devs. It helps a ton as projects grow into the hundreds of files and allows TDD to fly for the crazy networking mobile edge cases. In my team's most recent project, it lowered bugs during pre-production by almost 6 man weeks.

    I believe Suice may be a great first step and showing more examples of MVC/MVP to share the light would be a solid second. I just need to present it much better through documentation and examples to help beginners easily understand it.
     
  30. disturbingon3

    disturbingon3

    Joined:
    Jul 6, 2012
    Posts:
    26
    I released Suice 2.0.0 just now: https://github.com/Disturbing/suice/tree/Version/2.0.0

    It solves two major issues:

    (1) Constructor circular dependencies are now possible! (DepA can ref DepB and DepB can ref DepA)
    (2) You can now make dynamic providers with injectable dependencies without defining factory class!
    (3) Performance improvement by 20%. Cleaned up old provider methods that Guice 1.x/2.x use to have.

    Code (CSharp):
    1.  
    2. // Your dependency / service / controller class
    3. public Constructor(IProvider<ListItem> itemProvider) {
    4.      itemProvider.Provide(); // Infinite amount of list items which have injected dependencies
    5. }
    6.  
    7. public class ListItem {
    8.     // Inject works because this class is getting thrown in via IProvider!
    9.     [Inject]
    10.     public ListItem(DepA depA, DepB depB) {
    11.         // Store deps for service references, etc.
    12.      
    13.     }
    14. }
     
    Last edited: Mar 6, 2016
  31. disturbingon3

    disturbingon3

    Joined:
    Jul 6, 2012
    Posts:
    26