The word “injection” is a word heard in the enterprise software development world. Service Locator for object injection in cMVVM. Unlike DI solutions in Java and .NET world, here our Locator manages the dependency relationship of the object within itself and obtains the “Resolve” corresponding to the Value externally in the Key forum.

Why Object Injection?

To put it briefly, not to trust a certain object. The truth is that “decoupling” cannot be fully appreciated until the software reaches a certain complexity. It is very difficult to understand this by writing the “Hello World” program, which is software development known as “Middle Eastern Ferhat S.” in the industry. So I show a simple example below:

Let’s say we have a “Foo” class and this class does some work by calling a method on the “SomeService” object.

public class Foo
{
    ISomeService _service;

    public Foo()
    {
        _service = new SomeService();
    }

    public void DoSomething()
    {
        _service.PerformTask();
        ...
    }
}

There is no apparent problem, but there is a hided danger, our Foo class is in a strong dependency on “SomeService”. When there is a change in needs, you will have to open the Foo class, find SomeService in the constructor, write the new service, and then compile, test etc. And if this is a live service, you will have to wait for a time when it is not busy.

Now let’s reduce the dependency one step:

public class Foo
{
    ISomeService _service;

    public Foo(ISomeService service)
    {
        _service = service;
    }

    public void DoSomething()
    {
        _service.PerformTask();
        ...
    }
}

Now our Foo class is fully decoupled from the “SomeService” service, which is a good sign. We get instance from class Foo as follows:

public class Bar
{
    public void DoSomething()
    {
        var foo = new Foo(new SomeService());
        foo.DoSomething();
        ...
    }
}

But now it has a strong dependency on Bar and its class “SomeService”. Although it is a “Middle Eastern Ferhat S.” technique to keep these dependencies until the compiler fails, this is not true. Then we try to do the following:

public class Bar
{
    ISomeService _service;

    public Bar(ISomeService service)
    {
        _service = service;
    }

    public void DoSomething()
    {
        var foo = new Foo(_service);
        foo.DoSomething();
        ...
    }
}

Good, bar is not dependent now, but how will Bar be used? We have created a vicious circle.

Solution with Spring Framework

In Java Spring, object injection is performed with the dependencies of Foo and Bar as per the example above, with the constructor or the setter method.

<!-- Create an object:Bar-->
<bean id="barId" class="com.xxx.Bar" >
	<property name="service" ref="someServiceId"></property>
</bean>

<!-- Create SomeService instance -->
<bean id="someServiceId" class="com.xxx.SomeService"></bean>

As you can see, Spring stores the dependencies in an XML format and gets them at runtime via the IoC container factory and injects the dependencies.
So, do we need to solve this problem with XML in Unity3D?

Service Locator for Object Injection

The truth is that there are many ways for object injection, dependecy injection “DI” is just one of them. In MVVM, the injection method is implemented with ServiceLocator.
So what is ServiceLocator?
In short, it is an intermediary that treats object dependencies as a Dictionary within itself and returns the object according to the value it receives as a Key.

Service Locator UML

Still missing one of the most important objects, the IoC container factory. All objects that need Inject are installed by this factory. Our three types of factories mentioned earlier will meet our needs for IoC.
First, let’s create a Dictionary for the Locator:

private static readonly Dictionary<Type, Func<object>> Container = new Dictionary<Type, Func<object>>();

As seen above, we used anonymous method as Value. The method will be run and the object will be returned when needed, this also provides the Lazy loading mechanism.
As we begin to write our Locator, we first consider Registery. Since we will store it as a Dictionary, we must first register. Our locator for singleton or transient object will be like this:

/// <summary>
/// For each request, only return a unique instance
/// </summary>
/// <typeparam name="TInterface"></typeparam>
/// <typeparam name="TInstance"></typeparam>
public static void RegisterSingleton<TInterface, TInstance>() where TInstance : class, new()
{
    Container.Add(typeof(TInterface), Lazy<TInstance>(FactoryType.Singleton));
}
/// <summary>
/// For each request, only return a unique instance
/// </summary>
/// <typeparam name="TInstance"></typeparam>
public static void RegisterSingleton<TInstance>() where TInstance : class, new()
{
    Container.Add(typeof(TInstance), Lazy<TInstance>(FactoryType.Singleton));
}
/// <summary>
/// For each request, return a different instance
/// </summary>
/// <typeparam name="TInterface"></typeparam>
/// <typeparam name="TInstance"></typeparam>
public static void RegisterTransient<TInterface, TInstance>() where TInstance : class, new()
{
    Container.Add(typeof(TInterface),Lazy<TInstance>(FactoryType.Transient));
}
/// <summary>
/// For each request, return a different instance
/// </summary>
/// <typeparam name="TInstance"></typeparam>
public static void RegisterTransient<TInstance>() where TInstance : class, new()
{
    Container.Add(typeof(TInstance),Lazy<TInstance>(FactoryType.Transient));
}

private static Func<object> Lazy<TInstance>(FactoryType factoryType) where TInstance : class, new()
{
    return () =>
    {
        switch (factoryType)
        {
            case FactoryType.Singleton:
                return _singletonObjectFactory.AcquireObject<TInstance>();
            default:
                return _transientObjectFactory.AcquireObject<TInstance>();
        }
    };
}

Retrieving the required object from the Locator via the key will be with the Resolve method, we see Resolve below:

/// <summary>
/// Get an instance from the container
/// </summary>
/// <returns></returns>
private static object Resolve(Type type)
{
    if (!Container.ContainsKey(type))
    {
        return null;
    }
    return Container[type]();
 }

Then we define it in a global initialization file as follows:

ServiceLocator.RegisterSingleton<IUnitRepository,UnitRepository>();

And to get the object in the bussines code, it is sufficient to define it as follows:

ServiceLocator.Resolve<IUnitRepository>();