简体   繁体   中英

Is it possible to implement dependency injection without using service locator at the start of an application?

I am fairly familiar with concepts of service locator and dependency injection, but there is one thing that gets me confused all the time, ie, to implement dependency injection for an application we must use some sort of service locator at the start. Please consider the following code,lets say we have some simple DAL class:

public class UserProviderSimple : IUserProvider
{
    public void CreateUser(User user)
    {
        //some code to user here
    }
}

And then in the Business Logig Layer we have some simple class that uses IUserProvider that is injected using constructor injection:

public class UserServiceSimple : IUserService
{
    public IUserProvider UserProvider { get; set; }
    public UserServiceSimple(IUserProvider userProvider)
    {
        UserProvider = userProvider;
    }
    public void CreateUser(User user)
    {
        UserProvider.CreateUser(user);
    }
}

Now we may have couple of classes like that and use constructor injection everywhere, but in the main class where the application starts, all these types have to be resolved anyway, hence we must use a service locator to resolve all these types, for example, here I will create a singleton service locator class to resolve all the dependencies at the start of a console application like this:

public class ServiceLocator
    {
        private readonly UnityContainer _container;

        private static ServiceLocator _instance;

        public static ServiceLocator Instance()
        {
            if (_instance == null)
            {
                _instance = new ServiceLocator();
                return _instance;
            }
            return _instance;
        }

        private ServiceLocator()
        {
            _container = new UnityContainer();
            _container.RegisterType<IUserProvider, UserProviderSimple>();
            _container.RegisterType<IUserService, UserServiceSimple>();
        }

        public T Resolve<T>()
        {
            return _container.Resolve<T>();
        }
    }
    class Program
    {
        private static IUserService _userService;
        private static void ConfigureDependencies()
        {
            _userService = ServiceLocator.Instance().Resolve<IUserService();
        }

        static void Main(string[] args)
        {
            ConfigureDependencies();
        }
    }

So it seems like some kind of service locator is always used at the start of the application, hence using service locator is inevitable and it's not correct to always call it an anti-patern right (unless it's used not in the root of the application)?

You misunderstand what a Service Locator is. You do understand the part thatit is an anti-pattern , which is good, but what you're missing is that the pattern is not about the mechanics, but the role it plays in the application. In other words:

A DI container encapsulated in a Composition Root is not a Service Locator - it's an infrastructure component.

There is nothing inherently wrong with calling the class encapsulating the DI container bootstrapping code ServiceLocator , but you could also call it a Startup , Bootstrap or ContainerWrapper , it is just a naming convention.

On the other hand ServiceLocator as a design pattern is usually considered an anti-pattern since it becomes a hard dependency for the rest of the code and makes changes and testing hard and unpredictable. In your code it is Resolve<T> method which you would want to stay away from to avoid the consequences.

https://en.m.wikipedia.org/wiki/Service_locator_pattern

And to answer your question, a piece of code is usually required to initialize the DI container in any case even when it is hidden from you as part of a bigger DI framework itself, some frameworks though allow configuring your container from the configuration file too. Hope it helps!

I have struggled with the same question for quite some time. I have make the experience that you usually do not need a ServiceLocator (btw: best description of this anti patternhere and what you can do to avoid it in the corresponding, very awsome, book ).

Please see the refactoring of your code below. The basic idea here is that you have just one root object that acts as the composition root ( Program ) and all child dependencies of the complex object graph below that root are automatically resolved by the container.

public class Bootstrapper
{
    private readonly UnityContainer _container;

    private Bootstrapper()
    {
        _container = new UnityContainer();
    }

    public Program Intialize()
    {
        this.ConfigureDependencies(UnityContainer container);
        return this.GetCompositionRoot();
    }

    private void ConfigureDependencies()
    {
        _container.RegisterType<IUserProvider, UserProviderSimple>();
        _container.RegisterType<IUserService, UserServiceSimple>();
        _container.RegisterType<Program, Program>();
    }

    private Program GetCompositionRoot()
    {
        return _container.Resolve<Program>();
    }
}

public class Program
{
    public Program(IUserService userService)
    {
        _userService = userService ?? throw AgrumentNullExcpetion(nameof(userService));
    }

    static void Main(string[] args)
    {
        var program = new Bootstrapper().Initialize();
        program.Run();
    }

    public void Run()
    {
        // Do your work using the injected dependency _userService
        // and return (exit) when done.
    }
}

There are some situations where it does not fit, then YES it's an anti pattern.

We have to look if there are valid usages of the patterns, and for Service Locator there are several use cases.

In a typical line of business application, you should avoid the use of service. It should be the pattern to use when there are no other options.

For instance, inversion of control containers would not work without service location. It's how they resolve the services internally.

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