Search Unity

[Released] Adic - Dependency Injection Container

Discussion in 'Assets and Asset Store' started by Intentor, Mar 18, 2015.

  1. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94

    Another Dependency Injection Container for Unity 3D and beyond

    Adic is a lightweight dependency injection container for Unity 3D and any C# (or .Net) project.

    Based on the proof of concept container from Sebastiano Mandalà and studies of StrangeIoC, the intention of the project is to create a dependency injection container that is simple to use and extend, having on its roots the simplicity of the work of Mandalà and the extensibility of StrangeIoC, also borrowing some ideas from the classic Unity Application Block.

    The project is compatible with Unity 3D 5 and 4 and possibly 3 (not tested) and should work on all available platforms (tested on Windows/Mac/Linux, Android, iOS and Web Player).

    Asset Store: http://u3d.as/caZ
    Documentation and source code: https://github.com/intentor/adic/
    Issues: https://github.com/intentor/adic/issues

    Key features
    • Bind types, singleton instances, factories, game objects and prefabs.
    • Instance resolution by type, identifier and complex conditions.
    • Injection on constructor, fields and properties.
    • Can inject multiple objects of the same type.
    • Can resolve and inject instances from types that are not bound to the container.
    • Fast dependency resolution with internal cache.
    • Use of attributes to indicate injections, preferable constructors and post constructors.
    • Can be easily extended through extensions.
    • Framework decoupled from Unity all Unity based API is achieved through extensions.
    • Organized and well documented code written in C#.
    Feedback and support

    Have a question? Please post it here in the thread or contact me through email (see below).

    Found a bug?
    Please report it here in the thread or create an issue on the GitHub project page. You can also send a pull request if you have a fix or extension.

    To discuss more obscure, advanced or secret matters, you're welcome to send a message to support@intentor.com.br.
     
    one_one likes this.
  2. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.1 version is available to download on the GitHub project page and soon from the Asset Store.

    Changelog

    Framework
    • Added overloads by generics and type instance at IBindingFactory.ToFactory().
    Commander extension
    • Added interfaces to define command dispatchers (ICommandDispatcher) and command pools (ICommandPool).
    • Added System.IDisposable interface to the CommandDispatcher so it can release commands when the scene is destroyed.
    • Removed Dispose() method from Adic.Command. To make commands disposable, implement System.IDisposable.
    • Fixed dispose of commands that implemented System.IDisposable.
    • Fixed release of commands that implemented Adic.IUpdatable.
    • Now the Commander extension always depends on the Event Caller extension.
    Event Caller extension
    • Added evaluation of important disposable objects (IInjectorContainer and ICommandDispatcher) during registration to avoid the need for following a specific order when registering extensions.
    • Fixed adding duplicate objects in disposable/updateable lists.
     
    Last edited: Apr 14, 2015
  3. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.2 version is available on the Asset Store.

    Changelog

    Bindings Printer Extension
    • Fixed displaying of window when no containers exist.
    Bindings Setup Extension
    • Added extension.
    Commander extension
    • Added method GetCommandDispatcher() as extension to IInjectionContainer.
    Unity Binding Extension
    • Added ToResource() method, which binds to any asset (not necessarily a prefab).
    • Updated error messages.
     
    Last edited: Apr 14, 2015
  4. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.3 version is available on the Asset Store.

    Changelog

    Commander extension
    • Added TimedCommandDispatch and CommandDispatch components.
    • Added method ContainsRegistration() to Adic.ICommandDispatcher for checking whether a dispatcher contains a given command registration.
    • Added method GetAllRegistrations() to Adic.ICommandDispatcher to list all registered commands in a dispatcher.
    • Updated registration of commands using namespaces to find commands in children namespaces.
     
    Last edited: Apr 14, 2015
  5. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.5 version is available on the Asset Store.

    Changelog

    Framework
    • Added creation and injection of factories by the container. [Issue #4]
    Commander extension
    • Updated CommanderExtension.RegisterCommands() to use TypeUtils.GetAssignableTypesInNamespace() to get commands from the current assembly(ies).
     
    Last edited: Apr 14, 2015
  6. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.6 version is available on the Asset Store.

    Changelog

    Framework
    • Added overloads to SetupBindings() method to setup bindings from namespaces. [Issue #5]
    • Added BindingPriority attribute to indicate prioritary bindings setups when using SetupBindings() with namespaces. [Issue #5]
    • Removed factoryType property from Adic.IFactory. [Issue #6]
    • Fixed comments in examples.
    Bindings Setup Extension
    • Added Bindings Setup example.
     
  7. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.7 version is available on the Asset Store.

    Changelog

    Framework
    • Added manual resolution by identifier. [Issue #9]
    • Changed identifier type to string. [Issue #9]
    • Removed new() keyword form IBindingFactory.ToFactory(), given factories are now resolved by the container.
    • Fixed null reference with conditions. [Issue #10]
    Examples
    • Added example for factories. [Issue #8]
    • Updated comments in examples.
    • Updated organization of resources' files to avoid name crosstalk when loading resources in examples.
     
  8. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.8 version is available on the Asset Store.

    Changelog

    Framework
    • Added chaining for bindings. [Issue #11]
    • Updated instantiations, post construct callings and field/property setters to use cached methods (up to 100% in performance gain when resolving!).
    • Updated InjectionContainer to inherit from Adic.Injector.
    Context Root extension
    • Added generic version of AddContainer() to make container addition less verbose.
     
  9. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.9 version is available on the Asset Store.

    Changelog

    Framework
    • Added Adic.Binding.MultipleBindingConditionFactory for handling conditions of multiple bindings at the same time. [Issue #13]
    • Updated Adic.Binding.BindingConditionFactory to Adic.Binding.SingleBindingConditionFactory. [Issue #13]
    • Fixed instantiation of different instances for singleton bindings. [Issue #15]
    Commander extension
    • Added InvokeDispatch() method. [Issue #12]
    • Added Command.Invoke() method. [Issue #12]
    • Added Command.StartCoroutine() and Command.StopCoroutine() methods. [Issue #12]
    • Fixed registration attempt of abstract commands.
    Event Caller extension
    • Updated container extension to have a singleton instance of the Event Caller MonoBehaviour. [Issue #12]
    Unity Binding Extension
    • Added ToGameObjectsWithTag() method. [Issue #13]
     
    twobob likes this.
  10. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.10 version is available on the Asset Store.

    Changelog

    Framework
    • Added identifier to containers.
    • Fixed JIT compilation error when building to IOS (which compiles using AOT). [Issue #16]
    Commander extension
    • Added verification of already pooled commands when pooling.
    Mono Injection extension
    • Added InjectFromContainer attribute to select, by identifier, from which container injections should occur.
     
    twobob likes this.
  11. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,783
    Hi,

    Am curious, how are people using this with Unity ? What sorts of problems are they solving ?

    I can see you are putting a lot of work in here, and I suspect providing use cases will get you some more engagement :)

    Cheers,
    Adam.
     
  12. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Thanks for the comment! What you wrote is something that is really a deficiency of the documentation. It lacks information about why a container like Adic is useful and some use cases that exemplify that usefulness. I opened an issue in the project page to improve the documentation in regard to it.

    Adic is a pet project that grew a little larger than expected - so I decided that it'd be nice to share it with other devs. I use it on all game projects of my company, and most of its features come from the needs of these projects, with the other part being feedback and requests from other devs that uses it.

    To begin providing a better documentation, there is some information below that may help you understand why a dependency injection (DI) container such as Adic is useful in any kind of project.

    Why use a DI container?

    In a nutshell, to decouple your code.

    A DI container, in pair with a good architecture, can ensure SOLID principles and help you write better code.

    Using such container, you can easily work with abstractions without having to worry about the specifics of each external implementation, focusing just on the code you are writing. It's all related to dependencies: any dependency your code needs is not resolved directly by your code, but externally, allowing your code to deal only with its responsibilities.

    As a plus, there are other benefits from using a DI container:
    1. Refactorability: with your code decoupled, it's easy to refactor it without affecting the entire system.
    2. Reusability: thinking about abstractions allows your code to be even more reusable by making it small and focused on a single responsibility.
    3. Testability: by focusing on abstractions and dependency injection, it's easy to change implementations to test your code.
    4. Improved architecture: your codebase will be naturally better and more organized because you'll start to think about the relationships of your code.
    5. Staying sane: by focusing on small parts of the code and having a consistent architecture, the sanity of the developer is also ensured!
    Why use it with Unity?

    Unity is not SOLID friendly out of the box. Even the official examples may give a wrong idea on how to code on Unity. Using a DI container in conjunction with Unity it's possible to write code that is more extensible, reusable and less MonoBehaviour centric (in most cases, a regular class can do just fine or better).

    This way your code can become more modular and your components less tightly coupled to each other.

    Common use case

    Imagine you class depend on a given service that provides some action it may need:

    Code (CSharp):
    1. public class MyClass {
    2.     public void DoAction() {
    3.         var service = new SomeService();
    4.         service.SomeAction();
    5.     }
    6. }
    If in the future you need to change the implementation of the service, you'll have to get back to the class and change it. It can work just fine for small projects, but as the codebase grows, it can become a (error prone) nightmare to chase all these references.

    So, you can change it to a more decoupled code, so MyClass don't need to worry about the specific implementation of the SomeService it uses:

    Code (CSharp):
    1. public class MyClass {
    2.     private IService service;
    3.  
    4.     public MyClass(IService service) {
    5.         this.service = service;
    6.     }
    7.  
    8.     public void DoAction() {
    9.         this.service.SomeAction();
    10.     }
    11. }
    The ideia is that you invert the resolution of the dependency up into the execution flow.

    Now, any class that needs to use MyClass also has to provide an IService reference to it by constructor:

    Code (CSharp):
    1. public class MyOtherClass {
    2.     private IService service;
    3.  
    4.     public MyOtherClass(IService service) {
    5.         this.service = service;
    6.     }
    7.  
    8.     public void DoAction() {
    9.         var myClass = new MyClass(this.service)
    10.         myClass.DoAction();
    11.     }
    12. }
    But you could write it even better: given MyOtherClass depends only on MyClass (IService is just a tramp variable - a variable that is there just to be passed to other object), there's no need to store a reference to the IService abstraction:

    Code (CSharp):
    1. public class MyOtherClass {
    2.     private MyClass myClass;
    3.  
    4.     public MyOtherClass(MyClass myClass) {
    5.         this.myClass = myClass;
    6.     }
    7.  
    8.     public void DoAction() {
    9.         this.myClass.DoAction();
    10.     }
    11. }
    However, any class that uses MyOtherClass must also fullfill any dependencies it needs, again up into the execution flow, until a place where all the dependencies are resolved. This place is called the composition root.

    And that's where a DI container come in handy. In the composition root, a DI container is created and configured to resolve and wire all dependencies of any objects used by your code so you don't have to worry about it!

    Further reading
    Hope that makes you (and other devs) interest on using a DI container in your projects!
     
    Last edited: May 18, 2015
    AbgaryanFX likes this.
  13. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,783
    Thanks for a great response :) I use DI in other projects of mine that run across platforms to implement generic functionality in a platform specific way. Its very powerful.

    My next suggestion is to give an example of its usage in Unity games - perhaps by showing off an example that solves a common problem and explaining why your solution is good. Taking it out of the abstract and into the real might make it easier for some people to understand.
     
    one_one likes this.
  14. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Fantastic! DI is an amazing way to work with generic code implemented uniquely by plataform. In fact, I'll even add that to the docs as a benefit of using a DI container!

    I opened another issue to update the docs with a more real use case scenario for a DI container. In fact, one of my future plans is to create a full game example to be bundled to the framework.

    Thanks for the feedback!

    As a curiosity, do you use Adic in any of your projects?
     
  15. AdamGoodrich

    AdamGoodrich

    Joined:
    Feb 12, 2013
    Posts:
    3,783
    Nope :)

    During the day I run a startup that helps manage distributed work forces using mobile technology, and I play with Unity in my spare time and have developed quite a passion for it.

    Am now quite close to releasing a terrain generation, texturing and planting asset to the asset store - see attached image to get a sense of the quality.

    Because I want to release an asset I tend not to touch other peoples work - but if i was doing this purely to make games then I would definitely have a play with it.
     

    Attached Files:

  16. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Fantastic project, Adam! The quality is astonishing!

    I'm looking forward to it!

    And if you end up using Adic and need some help or have some other feedback... just let me know!
     
    AdamGoodrich likes this.
  17. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.11 version is available on the Asset Store.

    Changelog

    Framework
    • Added Unbind() method for identifiers. [Issue #24]
    • Added use of Inject attribute on constructor parameters. [Issue #19]
    Commander extension
    • Added automatic command retention when invoking or starting a coroutine. [Issue #21]
    • Added stopping of coroutines when releasing commands. [Issue #21]
    • Added release of commands by type. [Issue #18]
    • Implemented IDisposable interface on the command class. [Issue #21]
    Context Root extension
    • Fixed new instantiation of existing container's data on Awake().
     
  18. Deleted User

    Deleted User

    Guest

    Hi Intentor. You mentioned that you use cached methods for property getters and setters, and then you made it compile with AOT. Could you please tell if you went back to reflection, or is there a way to cache methods that works with AOT?
     
  19. Deleted User

    Deleted User

    Guest

    Just checked your code on GitHub, reflection it is.
     
  20. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    You were faster than me on answering the question! =D

    Adic uses IL generation on non AOT builds and method delegation from plain old reflection data on AOT builds.

    I also tried to use compiled expressions, because some of them (mysteriously) work on AOT despite the runtime compilation, but I couldn't create all needed expressions that way (most of them can't really compile).

    If someone is interested in looking into the implementation, here is the link to the source code: https://github.com/intentor/adic/blob/v2.11/src/Assets/Adic/Scripts/Framework/Util/MethodUtils.cs.

    Are you using Adic (or some other DI framework) on your projects or the question was just a curiosity?
     
  21. Deleted User

    Deleted User

    Guest

    Thanks for response. I have some setters to cache in my project, and search returned this thread. So I was wondering if you could somehow make it work.
     
  22. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    I hope the code on the component helped you!

    And if don't use any DI framework in your projects, take a look at Adic! It can be a life saver on a game project!
     
    Deleted User likes this.
  23. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.12 version is available on the Asset Store.

    Changelog

    Commander extension
    • Fixed pooling when registering from an empty command list.
    Context Root extension
    • Updated Init() method to be called on Unity's start event.
    Mono Injection extension
    • Fixed injection on MonoBehaviours that contain other attributes besides InjectFromContainer.
     
    Last edited: Jun 30, 2015
  24. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.13 version is available on the Asset Store.

    Binaries can be found on the version page.

    Changelog

    Commander extension
    • Fixed pooling when registering from an empty command list.
    Context Root extension
    • Updated Init() method to be called on Unity's start event.
    Mono Injection extension
    • Fixed injection on MonoBehaviours that contain other attributes besides InjectFromContainer.
     
  25. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    Hi there, especially given the name of your project, you are obviously aware of the other DI packages out there, not just StrangeIoC but also Zenject. Would you mind noting what specifically is different (and better? or worse in some cases?) about this project versus the others? Perhaps with some code or use case examples that really show the practical difference? I'm in the process of choosing a DI package right now and that would be very helpful!
     
  26. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello, Ghopper21!

    Adic has its strenghts and may have its own set of problems, but it's hard to speak about it without appearing biased! ;-)

    I do know other IoC projects on Unity, and at least for the two you cited, both are also fantastic! In fact, when I started Adic, I was also starting using Strange. But I wanted something that was even simpler to setup and use.

    Simplicity is always the motto with Adic. I know some of the concepts may seem rather complex at first, but everything I've created for the framework was trying to make it as simple as possible - and that I think it's one of its greatest strenghts.

    Performance is also something I always try to achieve, even with some limitations in platforms (mainly iOS) that prevent Adic from being way faster than it is.

    The support to injection in MonoBehaviours is also a very (very!) useful feature in Adic and something that I consider imperative to any IoC framework on Unity.

    Adic grows with the needs of the game projects I'm working on and from requests of the community, so expect any new features to arise from real use cases. Usually I try to release a new version every two weeks with all the fixes/additions I've made during that time.

    Speaking of code examples, take a look at the setup and hello world implementations of all main frameworks before choosing one. They teach you the basics and show how easy (or not!) can be using them. Below are links to some popular Unity's IoC frameworks (I took the liberty of including Adic among them!):
    In the end, I'd say all IoC frameworks on Unity are great on their own. I'd recommend you to try them all and choose the one that better suits your project and personal programming/organizational taste.

    Hope that helps!

    If you need any more information, feel free to reply to the post or send me a message!

    P.S.: after you do choose an IoC framework, It'd be fantastic if you could share what was your choice and why. This way you can help me making Adic even better!
     
    Last edited: Jul 10, 2015
    Ghopper21 likes this.
  27. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    Thanks @Intentor

    Question:
    Code (CSharp):
    1. container
    2.     .Bind<GameObject>().ToGameObject("Enemy1")
    3.     .Bind<GameObject>().ToGameObject("Enemy2")
    4.     .Bind<GameObject>().ToGameObject("Enemy3")
    5.     .Bind<GameObject>().ToGameObject("Enemy4")
    6.     .Bind<GameObject>().ToGameObject("Enemy5");
    Is just shorthand via chaining for:
    Code (CSharp):
    1. container.Bind<GameObject>().ToGameObject("Enemy1");
    2. container.Bind<GameObject>().ToGameObject("Enemy2");
    3. container.Bind<GameObject>().ToGameObject("Enemy3");
    4. container.Bind<GameObject>().ToGameObject("Enemy4");
    5. container.Bind<GameObject>().ToGameObject("Enemy5");
    Right? (You note you prefer the concision of the former; I prefer the parallelism of latter... :))

    p.s. will give fuller view as requested ASAP -- my initial impression is that Adic is the most lightweight and arguably the most Unity-native way of doing things compared to Strange and Zenject.
     
  28. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    Another question, re: this note in the doc:
    But I can annotate constructor parameters with [Inject] if I want? (For code explicitness.)

    Also, if there's no binding available for a particular injection, whether member or constructor, will Adic throw an exception or just use a Null value?
     
  29. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    I'm having a problem figuring out how command dispatch from GameObjects works, re:
    I have a (Canvas-style) UI Button. I've added the Command dispatch component to the Button game object and chosen one of the provided example commands. I drag the Command dispatch component into the Button's OnClick() UnityEvent delegate list in Inspector. But no DispatchCommand shows up as a choice for function. (CommandDispatch shows up in the Button's OnClick() function drop down, but the functions within that drop down are just the usual MonoBehavior methods, no sign of DispatchCommand.) What am I doing wrong? Thanks!
     
  30. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    Question about Adic.Command's coroutines: are these just hooking into Unity's coroutines system or is this a separate custom coroutine system specific to Adic?
     
  31. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello again, @Ghopper21! I'm glad you're enjoying Adic!

    Let me try to answer all your questions!

    Yeah, the first is just a shorthand for the latter. Both options are valid and their use is more a matter of personal taste!

    Every constructor parameter will always be fulfilled through injection. The Inject attribute is not required on this case, although it can be used to identify a named condition. However, there's no harm on keeping them just for code explicitness. But... be advised that following this will make you add Inject attributes on all constructor parameters of every class, given Adic will inject them all if you try to instantiate the type!

    If a binding is not available, Adic will try to instantiate the type using available bindings' data. This way, Adic can instantiate any type, even if it's not bound to the container.

    This sounds like a bug. I've just tested it and it's not working. I'll pull a fix and release a new version, maybe even today. Thanks!

    EDIT: it's already fixed. You can download the version on the Github project page.

    It's hooked up into Unity's regular coroutines system. It's just a convenient way to use them inside commands. :)

    Hope that helps! If you need any more details, just let me know!
     
    Last edited: Jul 11, 2015
  32. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    @Ghopper21, I've just released a new version of Adic with the fix for the bug you reported. You can download it on the Github project page.

    Soon it'll be also available on the Asset Store.
     
  33. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    Thanks -- pulled and all looks good! Appreciate the fast turn-around!
     
    Last edited: Jul 11, 2015
  34. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    Ah right. Ok, maybe that's useless explicitness. :)

    So a null value will be used for any unbound constructor parameters?

    Ah good. Was worried you were creating a parallel system. In general, I like how Adic seems to be as "Unity-native" as possible, working with Unity's existing provisions rather than against them.
     
  35. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    What still doesn't work is hooking e.g. a UI InputField's onValueChanged UnityEvent -- because it's still missing a 1-parameter version of DispatchCommand.

    Stepping back, the way Command parameters are handled isn't super clear in the doc. I see from the code and example that you can pass and receive an array of untyped objects. The lack of types on these is the first thing in Adic that I don't particularly like. Seems like a step backwards from e.g. the typed UnityActions.

    Stepping further back, I want to check I'm understanding the point of Commands. The purpose is to avoid an event pub/sub free-for-all where you can't see in one place who is responding to an event. Instead have everything triggered from a centralized Command -- with dependencies handled through injection (to maintain modularity). Adic handles injection for Commands automatically when you register a command.

    For hooking up UI UnityEvents in the Inspector, why not just use UnityActions (with typed parameters) instead of Commands? (I'm thinking out loud here.) Because then you'd have to create placeholder game objects with MonoBehaviors on them. Sure, you could use Adic's MonoBehavior injection to handle dependencies, but why create game objects when what you really want are collections of logic without visual representation -- i.e. a Command. Also, if you wanted to dispatch the command from code as opposed to from a UI event, you'd have to go find the game object, get the MonoBehavior, and call it that way -- yuck.

    So, if the above is correct, I see the point of Adic's Commands versus UnityActions -- but the loss of type-safe parameters is still unfortunate. Any thought about getting type-safe parameters at some point?

    I rambled a bit above :) so to recap my questions:
    1. Missing 1-parameter version of DispatchCommand (for use with e.g. UI InputField elements)?
    2. Is my general understanding of the point of Commands correct?
    3. Is my understanding of why use UI UnityEvents --> Command dispatch --> Commands instead of UI UnityEvents --> UnityActions correct?
    4. Any plans for type-safe Command parameters?
     
    Last edited: Jul 11, 2015
  36. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Hello again, @Ghopper21!

    First, thanks for the comments and ideas. It's always amazing when someone shares their uses/concerns/wows about Adic - it always leads to improvements!

    Now let me answer your questions!

    1. Missing 1-parameter version of DispatchCommand (for use with e.g. UI InputField elements)?
    This option is currently not available, although it's really important! However, and also answering 4, I opened up a new issue to check it out by implementing type safe commands on the next version.. The current implementation of commands (not strongly typed) is something that also bothers me, mainly because it has lead to some mixed type implementations that made some commands really complex to execute (i.e. the same command can receive different lists of parameters). If you need the CommandDispatch with a parameter ASAP, just contact me and I can send you a pre-release version at the beginning of the week.

    2. Is my general understanding of the point of Commands correct?
    Your understanding is correct. The ideia is to prevent a complex event system by centralizing all actions of an event in a single place (the command), without any visual logic (although you could call methods on views by injecting them).

    3. Is my understanding of why use UI UnityEvents --> Command dispatch --> Commands instead of UI UnityEvents --> UnityActions correct?
    It's correct. The idea is to hook up the UI UnityEvent with the CommandDispatch behaviour to call a command. However you could use an UnityAction based method to dispatch the command, if you prefer.

    The CommandDispatch is just a convenient way to display all available commands and to provide a public method to be called from the UI. You can also use a CommandReference variable in any MonoBehaviour to select a command from the inspector and dispatch it by code (but, in this case, not from the UI). See more details about it on the docs (look for From code using CommandReference type).

    Speaking about creating a new GameObject just for the CommandDispatch, something that I'm used to do is to attach the script on the UI element itself.

    4. Any plans for type-safe Command parameters?
    Yes! Already answered in 1.

    Another question that you asked in another post was about null values on constructor parameters. Given Adic will always try to resolve any type, there's no situation in which a constructor parameter can be null. If the type is an interface and there's no binding available, an exception will be thrown.

    And your observation about Adic being as Unity-native as possible is also correct. In fact, the base framework of Adic is agnostic (so it can be used outside Unity). Any Unity implementations are available through extensions, including the ToGameObject and other similar API routines.

    Hope that leads to a better understanding of Adic!

    If you have any more questions, feel free to ask them!
     
  37. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    Thanks for the answers! No rush on the 1-parameter CommandDispatch.

    Stepping even further back: the more I look at Adic, Zenject, and Strange, the more I wonder if there is an inherent fight against the "Zen" of Unity in these approaches -- which you can see in the way MonoBehaviour injection is handled. Unity basically wants to the object graph to be visualized in the Editor, with dependencies "injected" (so to speak) via serialized game object links set and visible in the Editor.

    With Adic, you extend MonoBehavior with an Inject method, which is elegant on the surface, especially as you can bake it into a custom MonoBehavior subclass as you recommend. However -- Inject is using a hard-coded reference to a static property on ContextData -- which is technically introducing a non-injected dependency and which some would say is really a "service locator" pattern (or anti-pattern, depending on who you ask).

    The way Zenject deals with MonoBehaviors is through a factory pattern, where you bind a game object type to a prefab in the context/container. Strange goes further to encourage a View/Mediator pattern where logic (and the set of dependencies) is pulled out from MonoBehaviors altogether.

    Not sure what conclusion to draw from these observations.
     
  38. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    @Ghopper21, this is always a complex discussion, a software architecture discussion - but something I love to discuss, because it always brings some interesting subjects, ideas and, above all, solutions.

    The idea of the Inject method is not to supress the use of the inspector - which is very useful - but to aid on providing references that not necessarily are serializable types (like interfaces). In this case, it really looks like a ServiceLocator (I can go even further by saying that DI looks like a fancy way of service locating). However, I've to say that I myself use it to provide different types of objects, including reference to view models, mostly to avoid GameObject.Find() calls, but never to inject references that I can just wire from the inspector directly. It's hard to say whether this is right/wrong, but from time to time I find myself thinking about it, and in a more general sense about suitable architectures for games on Unity.

    Since the first version of Adic, which requires both the Inject method call and the Inject attributes, I've been thinking if it's the best approach to handle injection, not only on MonoBehaviours, but throughout the framework. I do want the framework as decoupled as possible, in a way that it may look like "magic" (e.g. by not having the Inject attribute or any other special configurations on non-framework related classes).

    On Unity, I think DI shines when injecting on non MonoBehaviours objects. The Unity way of doing things is usually by creating tons of behaviours, even for actions that won't require them. I tend to prefer a more "regular class" approach, in which behaviours are only used when they really operate on the game object they are in.

    Speaking of Zenject, I think the it does a great job by injecting only on objects in the scene and through prefabs. Adic could also do that (when the Unity extension was created, I thought on injecting directly into the prefab. However, given the Mono Injection extension already existed, I kept it as the base way of doing this). I can go even further by saying this is something I'm considering for a 3.0 version of the framework, and our discussions here are just pushing me towards it even more.

    The View/Mediator on Strange is very interesting, but usually, at least for the projects in which I tried to use it, it always led more code than I wanted the team to write. That was one of the reasons I ended up using my own framework - I wanted to make things simple for the projects I was working on. In the end, the framework grew and I opted to release it - if it's useful to me, it should also be useful to others.

    Speaking of architecture, how do you usually organize your project code?

    And thanks for all ideas and observations! It also makes me find "holes" in the framework that I should analyse more carefully, helping me not only to make Adic better, but also to develop a better game architecture.
     
    Last edited: Jul 13, 2015
    Ghopper21 likes this.
  39. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.14.1 version is available on the Asset Store.

    Changelog

    Commander extension
    • Added CommandReference type to provide a dispatchable command reference on MonoBehaviours.
    • Fixed loading of commands on components when using binaries.
    • Fixed behaviours' public methods to provide a parameterless option. [Issue #28]
    Mono Injection extension
    • Added InjectionUtil class to group common routines from Mono and State Injection extensions.
    State Injection extension
    • Added extension.
     
  40. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    To me, this gets to the crux of the challenge. By accepting the serialization limits of the Editor, we're forced to either (1) have a split DI model where some things are "injected" via serialization and others in code, or (2) to avoid Inspector references altogether and do everything in code. Option 1 is inconsistent while 2 is explicitly working against the "zen of Unity."

    What I'm experimenting with is doubling down on the Editor/Inspector/serialization approach by NOT accepting the serialization limits (and NOT using any code-based DI framework at all). Specifically, I'm:
    • Using the Full Inspector package to get the ability to serialize interfaces (and other things) -- VFW is an alternative tool
    • Using Scriptable Object Factory to make creating singleton ScriptableObject assets in the Editor dead easy, just like creating any other Unity asset
    • Setting up the "object graph" as much as possible in the Editor, i.e. scene game objects, prefabs, and singleton ScriptableObject assets
    • Where I have to create game objects on the fly, I can create a factory singleton (as save it as a ScriptableObject asset like other singletons, and "inject" where needed via the interface serialization provided by Full Inspector)
    It's definitely a "poor man's DI" given I have to manage the injections manually within the Editor. But I use the Editor for tweaking gameplay settings and setting up visual elements, so it's nice to have everything "configurable" in the Editor.

    I'm sure there are problems and limitations with this approach, but so far I like how I can open up a scene, start with a "main" object, and visually "see" the entire object graph by following the Inspector links (including links to factories). For now, I'm going to push this approach as far as it can go and see what I learn... Love to hear your thoughts about this!
     
    Intentor likes this.
  41. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Mr. @Ghopper21!

    In a nutshell, given the components and the workflow you're using, I think it's a great approach! The only downside that I can think about it is that if you have to change a singleton service, you'll have to wire it manually anywhere it's used.

    In fact, I think the best architecture is always the one the team works best. If the architecture is too simple (and can "maybe" lead to future issues), but works fine for a given team, amazing! However, if the architecture is top notch, with all the best code and organization practices but the team has a hard time working with it, that's not good. It's important to look for the "sweet spot" between the simple (usually full of limitations) way of doing things and the best practices (that can add unnecessary complexity).

    Your view of the use of Inspector is noble. Given Unity provides it, and Unity itself is an entity framework, nothing better than coding and configuring using its own provided methods, by creating entities (game objects) with behaviours (the scripts) with their own exposed configurations through the Inspector.

    By using Full Inspector (great tool BTW, I didn't know it!) combined with ScriptableObjects (and some interfaces for a better abstraction), your "poor man's DI" seems a nice standardized way to work. I say "standard" because this way you have a single approach on handling dependencies, which makes it simpler to everyone on the team to work with it.

    When we have too many exceptions (or even just 1!) for a given action in a workflow (like "handling dependencies"), usually (and I can say that by experience), we tend to spend too much time thinking about what is exception ("this object should go through a DI container or exposed on the inspector?!"") then actually creating the game itself.

    Currently, I'm facing a DI related problem that, although not necessarily related to the Inspector, affects my current workflow on Unity (and it's leading to too many exceptions on the way we work).

    We started using PlayMaker in a project to handle UI flow and many other gameplay elements in a graphical way (our motto was always "code wisely", and I can assure you we were spending too much code on things that a simple state machine would fits best). By using it, the need for commands (and in-code dependencies) dropped significantly, because now a simple state machine can handle the work of many commands, and even better, graphically - so game designers and artists can now easily understand what's happening.

    The project is now using injection mostly for services (like data access, device configurations, etc.), and I'm thinking what to think about it, in a sense that "maybe" all those services shouldn't be using an in-code DI approach at all!

    But... I'll keep looking for the sweet spot!

    In the end, I believe we should think and code as simple as possible, always learning new things but also always analyzing whether those new things are really good for what we're doing and the way we work. So... if your workflow is currently the best for you, not staying in your way by introducing too many limitations, stick with it, but be always open for new approaches (which is your case, given you've been looking on improving the dependency managemement of your project)!
     
  42. Ghopper21

    Ghopper21

    Joined:
    Aug 24, 2012
    Posts:
    170
    Super -- thanks really encouraging given how thoughtful you are on this subject!

    Yes -- if this approach continues to work I may look into whipping up some kind of Editor tool that makes this easier, kind of an run-time search-and-replace (probably only if if the reference is null) based on some kind of master setting; basically bringing a bit of "binding" functionality into the editor.

    One of my considerations in deciding in the end to not use any formal DI/IoC framework was to make it easier for other team members, especially those that join the project down the road, to get up to speed. Now, whether my approach e.g. with ScriptableObjects-as-assets really will be easier to understand than a code-based DI framework remains to be seen.

    My general view of programming and architecture is highly influenced by the famous "Zen of Python" -- partly the substance of those ideas but party just the meta-idea that generally its best to find and follow and work with rather than against the native "way" of any system. It's clear Unity's "way" is GUI-first, code-second; trying to move more and more out of the Editor and into code seemed like going the opposite direction. It's a tough commitment to this "way," given how limited and frustrating the Editor's capabilities and design are -- but it's remarkably customizable as I realized in looking at tools like Full Inspector and VFX. Without tools like that -- and the Editor API that allows them to exist -- this approach would be impossible.

    Yeah, it's being pulled in two directions -- towards a more sophisticated and all encompassing GUI based approach (whether the standard Editor or PlayMaker) on one hand, towards a code-based framework on the other. For my current project I'm seeing if going with "GUI first, code second" will work; when the problems arise, my first thought will be to see if I can make an Editor customization or tool to enable what I need, rather than falling back on a code-first approach. (This is certainly not where I expected to end up when I was looking at Adic v Zenject v StrangeIoC a few days ago!) If I don't have this clear principle, I find that I go back and forth on each architectural decision and can end up with something truly inconsistent.

    I'm feeling good about it now -- let's see how I feel a week or a month from now! :)
     
  43. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Mr. @Ghopper21!

    Having a clear principle is imperative, case else you'll may be going back and forth and maybe just spend more time thinking than really creating the game!

    And, after your month testing this architecture, please share some thoughts about it! I'd love to hear!
     
  44. _svDvorak

    _svDvorak

    Joined:
    Jul 27, 2015
    Posts:
    3
    I'm finding your discussion @Intentor & @Ghopper21 incredibly interesting since I've having the same thoughts going around in my head. My first goal was to make a cleaner separation between Unity and my game logic but I've looked over Zenject & StrangeIoC and been feeling that I'm missing the ease of editing & configuration through the Unity editor with both those approaches. I'm looking forward to hear more from your current experiment Ghopper!
     
    Intentor and Ghopper21 like this.
  45. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    Glad you're having a good time reading all our discussion, @_svDvorak!

    If you have any thoughts on the subject you want to share, feel free to place them here!
     
    Ghopper21 likes this.
  46. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.15.1 version is available on the Asset Store.

    Changelog

    Framework
    • Added inject parameters on PostConstructor methods. [Issue #34]
    • Added catch for ReflectionTypeLoadException when reading assemblies for type inspection.
    Commander extension
    • Updated commands to be singleton by default. [Issue #31]
     
  47. killradio

    killradio

    Joined:
    Oct 1, 2012
    Posts:
    4
    Hello Intentor,

    I am interested in Adic for my project, as it looks really simple but still powerful for what I need.

    For the moment, I am using custom MonoBehaviours scripts to create and edit some of my "managers".

    For example, I have a game time clock that always runs and let me speed time, set the length of the ingame day etc.
    This clock is a custom script so I can use public properties to set the default values (ingame day length, ingame minutes in realtime seconds etc.) in the editor.

    I need this clock in quite some scripts (every thing that moves for example).

    Your framework seems perfect for what I need (Register my clock and let it be injected everywhere else).

    For the moment though, I am just testing some bindings and don't understand the output :(

    Here is what I am testing :

    Code (CSharp):
    1. public class GameContext : ContextRoot
    2.     {
    3.         public override void Init()
    4.         {
    5.             Debug.Log("Game context - INIT");
    6.         }
    7.  
    8.         public override void SetupContainers()
    9.         {
    10.             Debug.Log("Game context - SETUP CONTAINERS");
    11.             AddContainer<InjectionContainer>()
    12.                 .Bind<ModuleB>().ToGameObject("Module B");
    13.         }
    14.     }
    With ModuleB being the following :
    Code (CSharp):
    1. public class ModuleB : BaseMonoBehaviour
    2.     {
    3.         [PostConstruct]
    4.         private void PostConstruct()
    5.         {
    6.             Debug.Log("Module B - POSTCONSTRUCT");
    7.         }
    8.  
    9.         public override void Start()
    10.         {
    11.             base.Start();
    12.             Debug.Log("Module B - START");
    13.         }
    14.  
    15.         public void Awake()
    16.         {
    17.             Debug.Log("Module B - AWAKE");
    18.         }
    19.     }
    Nothing fancy. BaseMonoBehaviour calls this.Inject() inside Start() method.
    In my hierarchy, I have a game object called "Module B" with the ModuleB script attached to it.
    Now, in my console, here is what I've got :

    ----
    Module B - AWAKE
    Game context - SETUP CONTAINERS
    Module B - POSTCONSTRUCT
    Module B - POSTCONSTRUCT
    Module B - START
    Game context - INIT
    ----

    I understand the order (it follows the documentation on github) but PostConstruct is called twice.
    Is this an error or is there something I don't understand ?

    Also, am I correct to think that by doing the binding to GameObject("Module B"), every time a ModuleB will be injected, it will be the same ? (Singleton)

    Thanks for your help and good job for the framework ! :)

    Cheers.
     
  48. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    @killradio!

    I'm glad you're enjoying the framework! It's simple yet powerfull in its simplicity!

    About your problem with PostConstruct, it does look like a bug. I opened an issue (https://github.com/intentor/adic/issues/35) to check it out.

    If I release a new version today with the fix, I'll replay to the topic so you can download it from the GitHub project page.

    And your singleton understanding is correct. Everytime an instance of ModuleB is injected, it'll be the same (in the case, from Module B game object).

    Thanks for the report!
     
    Last edited: Jan 19, 2016
  49. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    @killradio!

    I've just released a new Adic version with the fix for the PostConstruct duplicate call: https://github.com/intentor/adic/releases/tag/v2.15.2 (soon it'll be available on the Asset Store).

    That was a critical bug, given it was injecting twice in some cases (like yours), which could lead not only to a performance drain, but also to wrong injections depending on when the user is creating the bindings.

    Thanks for reporting it!
     
  50. Intentor

    Intentor

    Joined:
    Jul 24, 2013
    Posts:
    94
    The 2.15.2 version is available on the Asset Store.

    Changelog

    Mono Injection extension
    • Fixed singleton MonoBehaviour duplicate injection call. [Issue #35]
     
    Chris-Clark likes this.